
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
AuthorizationManageren lugar de la antigua jerarquía deAccessDecisionManageryAccessDecisionVoter. - 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.RequestMatcherpersonalizado: reglas arbitrarias conrequest -> ....
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 autoridadROLE_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 conanyRequest().authenticated()oanyRequest().denyAll(). - Mezclar
hasRoleyhasAuthority:hasRole("ADMIN")equivale ahasAuthority("ROLE_ADMIN"). Si el IdP (Keycloak, Auth0, Azure AD) emite scopes sin prefijoROLE_, usahasAuthority("SCOPE_admin")o configura unJwtAuthenticationConverterque 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 conROLE_CLIENTE_BANCAy/api/admin/**a empleados con autoridades de back office, conanyRequest().denyAll()para imponer zero-trust. - Telco: separar por método HTTP para permitir
GETpúblico en el catálogo de tarifas y exigir autenticación enPOSTde altas. - Sector público: reglas por IP con
AuthorizationManagerpersonalizado para limitar endpoints sensibles a rangos de la red administrativa.
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.