I am with my first spring-boot project. I did succesfully configure it to check for authentication; if the user/password was wrong the method was not invoked (status 401 unauthorized), if it was right it succeeded.
Now I have added authorization with JSR250 and I am only getting 403 Access denied.
The WS:
@RestController
@RequestMapping("/password")
public class ServicioPassword {
@GetMapping(path = "ldap")
public ResponseEntity<String> getLdap() {
var authentication = SecurityContextHolder.getContext().getAuthentication();
System.out.println("EN LDAP " + authentication.getPrincipal() + " - " + authentication.isAuthenticated());
for (var authority : authentication.getAuthorities()) {
System.out.println("Authority= " + authority);
}
return ResponseEntity.ok("DE LDAP");
}
When invoked, I get this on console:
EN LDAP LdapUserDetailsImpl [Dn=cn=ivr_apl_user,ou=IVR,ou=Aplicaciones,dc=pre,dc=aplssib; Username=ivr_apl_user; Password=[PROTECTED]; Enabled=true; AccountNonExpired=true; CredentialsNonExpired=true; AccountNonLocked=true; Granted Authorities=[AGNI_OIMIVR]] - true
Authority= AGNI_OIMIVR
Yet, if I add @RolesAllowed("AGNI_OIMIVR")
, when I invoke it I get a 403 Forbidden.
The MethodSecurityConfig
:
@Configuration
@EnableGlobalMethodSecurity(jsr250Enabled = true, prePostEnabled = true)
public class MethodSecurityConfig
extends GlobalMethodSecurityConfiguration{
}
I have kept the WebSecurityConfig
:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Autowired
private Environment environment;
@Bean
BindAuthenticator bindAuthenticator(
final BaseLdapPathContextSource contextSource) {
var bindAuthenticator = new BindAuthenticator(contextSource);
bindAuthenticator.setUserDnPatterns(new String[]{environment.getRequiredProperty("spring.ldap.userdnpattern")});
return bindAuthenticator;
}
@Bean
AuthenticationProvider ldapAuthenticationProvider(
final LdapAuthenticator ldapAuthenticator) {
var ldapAuthenticationProvider = new LdapAuthenticationProvider(ldapAuthenticator);
var ldapUserDetailsMapper = new CustomUserDetailsMapper();
var ldapMemberRoles = environment.getRequiredProperty("spring.ldap.roleattributes");
ldapUserDetailsMapper.setRoleAttributes(ldapMemberRoles.split(","));
ldapUserDetailsMapper.setRolePrefix("");
ldapAuthenticationProvider.setUserDetailsContextMapper(ldapUserDetailsMapper);
return ldapAuthenticationProvider;
}
@Bean
SecurityFilterChain filterChain(
final HttpSecurity http)
throws Exception {
http.csrf().disable()
.cors().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER).and()
.authorizeRequests()
.anyRequest().authenticated().and()
.httpBasic();
return http.build();
}
UPDATE: Adding log after setting logging.level.org.springframework.security=TRACE
:
Note that the line: 2022-07-07 13:04:27.464 WARN 81968 --- [nio-8080-exec-2] e.s.d.o.s.ws.CustomUserDetailsMapper : createAuthority agni_oimivr
comes from a log from one of my custom classes.
2022-07-07 13:04:27.441 TRACE 81968 --- [nio-8080-exec-2] o.s.s.w.a.www.BasicAuthenticationFilter : Found username 'ivr_apl_user' in Basic Authorization header
2022-07-07 13:04:27.442 TRACE 81968 --- [nio-8080-exec-2] o.s.s.authentication.ProviderManager : Authenticating request with LdapAuthenticationProvider (1/1)
2022-07-07 13:04:27.444 TRACE 81968 --- [nio-8080-exec-2] o.s.s.l.a.BindAuthenticator : Attempting to bind as cn=ivr_apl_user,ou=[REDACTED]
2022-07-07 13:04:27.444 TRACE 81968 --- [nio-8080-exec-2] s.s.l.DefaultSpringSecurityContextSource : Removing pooling flag for user cn=ivr_apl_user,ou=[REDACTED]
2022-07-07 13:04:27.463 DEBUG 81968 --- [nio-8080-exec-2] o.s.s.l.a.BindAuthenticator : Bound cn=ivr_apl_user,ou=[REDACTED]
2022-07-07 13:04:27.463 DEBUG 81968 --- [nio-8080-exec-2] o.s.s.l.u.LdapUserDetailsMapper : Mapping user details from context with DN cn=ivr_apl_user,ou=[REDACTED]
2022-07-07 13:04:27.464 WARN 81968 --- [nio-8080-exec-2] e.s.d.o.s.ws.CustomUserDetailsMapper : createAuthority agni_oimivr
2022-07-07 13:04:27.464 DEBUG 81968 --- [nio-8080-exec-2] o.s.s.l.a.LdapAuthenticationProvider : Authenticated user
2022-07-07 13:04:27.465 DEBUG 81968 --- [nio-8080-exec-2] o.s.s.w.a.www.BasicAuthenticationFilter : Set SecurityContextHolder to UsernamePasswordAuthenticationToken [Principal=LdapUserDetailsImpl [Dn=cn=ivr_apl_user,ou=IVR,ou=Aplicaciones,dc=pre,dc=aplssib; Username=ivr_apl_user; Password=[PROTECTED]; Enabled=true; AccountNonExpired=true; CredentialsNonExpired=true; AccountNonLocked=true; Granted Authorities=[AGNI_OIMIVR]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[AGNI_OIMIVR]]
2022-07-07 13:04:27.465 TRACE 81968 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Invoking RequestCacheAwareFilter (7/12)
2022-07-07 13:04:27.465 TRACE 81968 --- [nio-8080-exec-2] o.s.s.w.s.HttpSessionRequestCache : No saved request
2022-07-07 13:04:27.465 TRACE 81968 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderAwareRequestFilter (8/12)
2022-07-07 13:04:27.466 TRACE 81968 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Invoking AnonymousAuthenticationFilter (9/12)
2022-07-07 13:04:27.466 TRACE 81968 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter : Did not set SecurityContextHolder since already authenticated UsernamePasswordAuthenticationToken [Principal=LdapUserDetailsImpl [Dn=cn=ivr_apl_user,ou=IVR,ou=Aplicaciones,dc=pre,dc=aplssib; Username=ivr_apl_user; Password=[PROTECTED]; Enabled=true; AccountNonExpired=true; CredentialsNonExpired=true; AccountNonLocked=true; Granted Authorities=[AGNI_OIMIVR]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[AGNI_OIMIVR]]
2022-07-07 13:04:27.466 TRACE 81968 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Invoking SessionManagementFilter (10/12)
2022-07-07 13:04:27.467 TRACE 81968 --- [nio-8080-exec-2] s.CompositeSessionAuthenticationStrategy : Preparing session with ChangeSessionIdAuthenticationStrategy (1/1)
2022-07-07 13:04:27.467 DEBUG 81968 --- [nio-8080-exec-2] w.c.HttpSessionSecurityContextRepository : The HttpSession is currently null, and the HttpSessionSecurityContextRepository is prohibited from creating an HttpSession (because the allowSessionCreation property is false) - SecurityContext thus not stored for next request
2022-07-07 13:04:27.467 TRACE 81968 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Invoking ExceptionTranslationFilter (11/12)
2022-07-07 13:04:27.467 TRACE 81968 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Invoking FilterSecurityInterceptor (12/12)
2022-07-07 13:04:27.468 TRACE 81968 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor : Did not re-authenticate UsernamePasswordAuthenticationToken [Principal=LdapUserDetailsImpl [Dn=cn=ivr_apl_user,ou=IVR,ou=Aplicaciones,dc=pre,dc=aplssib; Username=ivr_apl_user; Password=[PROTECTED]; Enabled=true; AccountNonExpired=true; CredentialsNonExpired=true; AccountNonLocked=true; Granted Authorities=[AGNI_OIMIVR]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[AGNI_OIMIVR]] before authorizing
2022-07-07 13:04:27.468 TRACE 81968 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor : Authorizing filter invocation [GET /password/ldap] with attributes [authenticated]
2022-07-07 13:04:27.469 DEBUG 81968 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor : Authorized filter invocation [GET /password/ldap] with attributes [authenticated]
2022-07-07 13:04:27.470 TRACE 81968 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor : Did not switch RunAs authentication since RunAsManager returned null
2022-07-07 13:04:27.470 DEBUG 81968 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Secured GET /password/ldap
2022-07-07 13:04:27.471 TRACE 81968 --- [nio-8080-exec-2] o.s.s.a.i.a.MethodSecurityInterceptor : Did not re-authenticate UsernamePasswordAuthenticationToken [Principal=LdapUserDetailsImpl [Dn=cn=ivr_apl_user,ou=IVR,ou=Aplicaciones,dc=pre,dc=aplssib; Username=ivr_apl_user; Password=[PROTECTED]; Enabled=true; AccountNonExpired=true; CredentialsNonExpired=true; AccountNonLocked=true; Granted Authorities=[AGNI_OIMIVR]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[AGNI_OIMIVR]] before authorizing
2022-07-07 13:04:27.472 TRACE 81968 --- [nio-8080-exec-2] o.s.s.a.i.a.MethodSecurityInterceptor : Authorizing ReflectiveMethodInvocation: public org.springframework.http.ResponseEntity es.ssib.dtic.oimivr.service.ws.v1.ServicioPassword.getLdap(); target is of class [es.ssib.dtic.oimivr.service.ws.v1.ServicioPassword] with attributes [ROLE_AGNI_OIMIVR]
2022-07-07 13:04:27.475 TRACE 81968 --- [nio-8080-exec-2] o.s.s.a.i.a.MethodSecurityInterceptor : Failed to authorize ReflectiveMethodInvocation: public org.springframework.http.ResponseEntity es.ssib.dtic.oimivr.service.ws.v1.ServicioPassword.getLdap(); target is of class [es.ssib.dtic.oimivr.service.ws.v1.ServicioPassword] with attributes [ROLE_AGNI_OIMIVR] using AffirmativeBased [DecisionVoters=[org.springframework.security.access.annotation.Jsr250Voter@6797e2e2, org.springframework.security.access.vote.RoleVoter@2ab76862, org.springframework.security.access.vote.AuthenticatedVoter@152f6a2e], AllowIfAllAbstainDecisions=false]
2022-07-07 13:04:27.484 TRACE 81968 --- [nio-8080-exec-2] o.s.s.w.a.ExceptionTranslationFilter : Sending UsernamePasswordAuthenticationToken [Principal=LdapUserDetailsImpl [Dn=cn=ivr_apl_user,ou=IVR,ou=Aplicaciones,dc=pre,dc=aplssib; Username=ivr_apl_user; Password=[PROTECTED]; Enabled=true; AccountNonExpired=true; CredentialsNonExpired=true; AccountNonLocked=true; Granted Authorities=[AGNI_OIMIVR]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[AGNI_OIMIVR]] to access denied handler since access is denied
org.springframework.security.access.AccessDeniedException: Acceso denegado
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:73) ~[spring-security-core-5.7.1.jar:5.7.1]
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.attemptAuthorization(AbstractSecurityInterceptor.java:239) ~[spring-security-core-5.7.1.jar:5.7.1]
[...]
2022-07-07 13:04:27.497 DEBUG 81968 --- [nio-8080-exec-2] o.s.s.w.access.AccessDeniedHandlerImpl : Responding with 403 status code
What am I doing wrong?
The Authentication
object of your authenticated user is:
UsernamePasswordAuthenticationToken [Principal=LdapUserDetailsImpl [Dn=cn=ivr_apl_user,ou=IVR,ou=Aplicaciones,dc=pre,dc=aplssib; Username=ivr_apl_user; Password=[PROTECTED]; Enabled=true; AccountNonExpired=true; CredentialsNonExpired=true; AccountNonLocked=true; Granted Authorities=[AGNI_OIMIVR]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[AGNI_OIMIVR]]
Note that the GrantedAuthorities
is Granted Authorities=[AGNI_OIMIVR]
, there is no ROLE_
prefix there. When you add @RolesAllowed("AGNI_OIMIVR")
to the method, the ROLE_
prefix will be added automatically to the authority that you passed as an argument to the annotation, becoming ROLE_AGNI_OIMIVR
.
Spring Security will try to match ROLE_AGNI_OIMIVR
that is in the annotation with AGNI_OIMIVR
that is in the granted authorities' property, but they do not match.
You have three options:
ROLE_
prefixGrantedAuthorityDefaults
removing the rolePrefix
, like so:@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("");
}
@PreAuthorize("hasAuthority('AGNI_OIMIVR')")
Another tip would be to use the new @EnableMethodSecurity(jsr250Enabled = true)
which uses the simplified AuthorizationManager
API, improve logging, amongst others.