authorizeHttpRequests y reglas de acceso

Intermedio
Spring Boot
Spring Boot
Actualizado: 04/05/2026

Diagrama: tutorial-spring-security-authorize-http-requests

Del antiguo authorizeRequests al nuevo

Spring Security 6 promueve authorizeHttpRequests como DSL canonico para autorizar peticiones. Reemplaza al antiguo authorizeRequests, que ha sido marcado como deprecated. El cambio incluye ventajas notables:

  • Evaluación perezosa con AuthorizationManager en lugar de la antigua jerarquía de AccessDecisionManager y AccessDecisionVoter.
  • Coherencia con el resto de DSL reactivos de Spring.
  • Mayor claridad de lectura.

Reglas por ruta

@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/", "/home", "/css/**", "/js/**").permitAll()
            .requestMatchers("/admin/**").hasRole("ADMIN")
            .requestMatchers(HttpMethod.POST, "/api/**").hasAnyRole("USER", "ADMIN")
            .requestMatchers(HttpMethod.GET, "/api/**").authenticated()
            .anyRequest().denyAll())
        .formLogin(Customizer.withDefaults())
        .build();
}

Las reglas se evalúan en orden. Cuando la primera coincide, su resultado decide el acceso. Es recomendable cerrar siempre con anyRequest().denyAll() o anyRequest().authenticated() para evitar fugas por configuración incompleta.

Patrones permitidos

requestMatchers acepta distintas formas de expresar el patrón:

  • Cadena con patrón Ant: "/api/usuarios/**".
  • HttpMethod + patrón: limitar a verbos concretos.
  • RequestMatcher personalizado: reglas arbitrarias con request -> ....

Desde Spring Security 6 ya no hay riesgo de colisión entre mvcMatchers y antMatchers: requestMatchers detecta automáticamente si hay MVC en el classpath y selecciona la implementación adecuada.

Roles y autoridades

  • hasRole("ADMIN"): requiere autoridad ROLE_ADMIN.
  • hasAnyRole("USER", "ADMIN"): cualquiera de los roles.
  • hasAuthority("SCOPE_read"): requiere la autoridad exacta, sin prefijo.
  • hasAnyAuthority(...): varias autoridades alternativas.

Si tu autorización se basa en scopes OAuth2 (SCOPE_read, SCOPE_write), utiliza hasAuthority para evitar el prefijo ROLE_ que añade hasRole.

AuthorizationManager personalizado

Para reglas complejas no expresables con SpEL, se implementa un AuthorizationManager:

AuthorizationManager<RequestAuthorizationContext> ownerOnly =
    (auth, ctx) -> {
        String usuario = ctx.getVariables().get("usuario");
        String username = auth.get().getName();
        return new AuthorizationDecision(username.equals(usuario));
    };

http.authorizeHttpRequests(auth -> auth
    .requestMatchers("/perfil/{usuario}").access(ownerOnly)
    .anyRequest().authenticated());

RequestAuthorizationContext expone las variables de ruta extraidas del patrón, lo que permite implementar reglas dependientes del recurso solicitado sin mezclar lógica en controladores.

Combinación con method security

authorizeHttpRequests protege por URL; @EnableMethodSecurity con @PreAuthorize protege por método. Ambas capas se combinan: la primera actúa como filtro grueso a nivel de ruta y la segunda expresa reglas finas sobre lógica de negocio. En proyectos corporativos es habitual habilitar las dos para lograr defensa en profundidad.

@Configuration
@EnableMethodSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .requestMatchers("/api/**").authenticated()
                .anyRequest().denyAll())
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
            .build();
    }
}

@Service
public class PedidoService {

    @PreAuthorize("hasRole('ADMIN') or @permisoService.esPropietario(authentication, #pedidoId)")
    public Pedido actualizar(Long pedidoId, PedidoUpdate cambios) {
        // logica de negocio
    }
}

Errores frecuentes en proyectos corporativos

  • Olvidar anyRequest(): sin esta regla final, peticiones no coincidentes quedan denegadas por defecto en Spring Security 6, pero el mensaje de error no siempre es claro. Siempre cierra con anyRequest().authenticated() o anyRequest().denyAll().
  • Mezclar hasRole y hasAuthority: hasRole("ADMIN") equivale a hasAuthority("ROLE_ADMIN"). Si el IdP (Keycloak, Auth0, Azure AD) emite scopes sin prefijo ROLE_, usa hasAuthority("SCOPE_admin") o configura un JwtAuthenticationConverter que anada el prefijo.
  • Orden incorrecto de reglas: Spring Security evalúa de arriba a abajo y deja de buscar en la primera coincidencia. Si pones .requestMatchers("/api/**").authenticated() antes de .requestMatchers("/api/public/**").permitAll(), la ruta pública quedara protegida.

Casos de uso B2B

  • Banca: restringir /api/transferencias/** a usuarios con ROLE_CLIENTE_BANCA y /api/admin/** a empleados con autoridades de back office, con anyRequest().denyAll() para imponer zero-trust.
  • Telco: separar por método HTTP para permitir GET público en el catálogo de tarifas y exigir autenticación en POST de altas.
  • Sector público: reglas por IP con AuthorizationManager personalizado para limitar endpoints sensibles a rangos de la red administrativa.
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Spring Boot es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Spring Boot

Explora más contenido relacionado con Spring Boot y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Sustituir authorizeRequests por authorizeHttpRequests. Definir reglas por ruta, método HTTP y rol. Componer reglas con AuthorizationManager personalizado.