403 Forbidden when introducing authorization on spring boot rest - spring-boot

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:
Change the role in LDAP to have the ROLE_ prefix
Expose a Bean of GrantedAuthorityDefaults removing the rolePrefix, like so:
#Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("");
}
Use #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.

Related

How to implement custom UserDetailsService or custom AuthenticationProvider in Spring authorization server 0.2.0

I am trying a new Spring Authorization server 0.2.0. I have managed to successfully run a sample application located at https://github.com/spring-projects/spring-authorization-server/tree/main/samples/boot/oauth2-integration.
Now, I am trying to add custom UserDetailsService to the authorization server. I have created a custom UserDetailsService with users saved in the Mysql database.
I have replaced this
#Bean
UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user1")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
with this
#Bean
UserDetailsService users() {
return new CustomUserDetailsService();
}
Now Application throws an error "No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken" while trying to login in "Authorization Code" grant flow.
I don't know where to add my custom AuthenticationProvider. I have tried adding to DefaultSecurityConfig as below. But authorization code grant flow always returns invalid_client.
#Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authenticationProvider(authenticationProvider)
.authorizeRequests(authorizeRequests ->
authorizeRequests.antMatchers("/actuator/**").permitAll()
.anyRequest().authenticated())
.formLogin(withDefaults());
return http.build();
}
I think, I missed something about client authentication here as my custom authenticationProvider is having to authenticate the method only for users but not clients.
Now my question is how to add custom AuthenticationProvider or CustomUserDetailsService without adding AuthenticationProvider to the Spring authorization server.
UPDATE:
Here are my CustomUserDetailsService and CustomAuthenticationProvider
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public DcubeUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Supplier<UsernameNotFoundException> s =
() -> new UsernameNotFoundException("Problem during authentication!");
DcubeUser u = userRepository.findUserByUsername(username).orElseThrow(s);
return new DcubeUserDetails(u);
}
}
#Service
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
CustomUserDetails user = userDetailsService.loadUserByUsername(username);
return checkPassword(user, password);
}
#Override
public boolean supports(Class<?> aClass) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass);
}
private Authentication checkPassword(CustomUserDetails user, String rawPassword) {
if (Objects.equals(rawPassword, user.getPassword())) {
return new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), user.getAuthorities());
} else {
throw new BadCredentialsException("Bad credentials");
}
}
}
Here is my log
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.s.w.s.HttpSessionRequestCache : Did not match request /error to the saved one DefaultSavedRequest [http://localhost:8888/oauth2/authorize?response_type=code&client_id=web-client&scope=web:write]
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderAwareRequestFilter (10/14)
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking AnonymousAuthenticationFilter (11/14)
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.s.w.a.AnonymousAuthenticationFilter : Did not set SecurityContextHolder since already authenticated UsernamePasswordAuthenticationToken [Principal=Ramesh, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=D4FF0763B895353AC334140528DE35CD], Granted Authorities=[ADMIN]]
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking SessionManagementFilter (12/14)
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking ExceptionTranslationFilter (13/14)
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking FilterSecurityInterceptor (14/14)
2021-10-27 09:08:34.387 DEBUG 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Secured GET /error
2021-10-27 09:08:34.464 DEBUG 10928 --- [nio-8888-exec-7] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2021-10-27 09:08:56.440 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda$1085/0x0000000801355c90#32e7df65, Filters=[org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#421d7900, org.springframework.security.web.context.SecurityContextPersistenceFilter#42a7e7e1, org.springframework.security.web.header.HeaderWriterFilter#55e88bc, org.springframework.security.web.csrf.CsrfFilter#20a116a0, org.springframework.security.web.authentication.logout.LogoutFilter#67e21ea2, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter#e2ee348, org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter#5d67bf4d, org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter#1477d4e6, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter#5ec3689b, org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter#448fa659, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#7c0a6f62, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter#4946dfde, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter#45964b9e, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#554e9509, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#34ea86ff, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#166a5659, org.springframework.security.web.session.SessionManagementFilter#4c12f54a, org.springframework.security.web.access.ExceptionTranslationFilter#417b3642, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#22ed2886, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter#76219fe, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter#4c599679, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter#1bcf2c64]] (1/2)
2021-10-27 09:08:56.440 DEBUG 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Securing POST /oauth2/authorize
2021-10-27 09:08:56.440 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (1/22)
2021-10-27 09:08:56.441 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking SecurityContextPersistenceFilter (2/22)
2021-10-27 09:08:56.441 TRACE 10928 --- [nio-8888-exec-8] w.c.HttpSessionSecurityContextRepository : Retrieved SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=Ramesh, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=D4FF0763B895353AC334140528DE35CD], Granted Authorities=[ADMIN]]] from SPRING_SECURITY_CONTEXT
2021-10-27 09:08:56.441 DEBUG 10928 --- [nio-8888-exec-8] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=Ramesh, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=D4FF0763B895353AC334140528DE35CD], Granted Authorities=[ADMIN]]]
2021-10-27 09:08:56.441 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (3/22)
2021-10-27 09:08:56.442 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking CsrfFilter (4/22)
2021-10-27 09:08:56.442 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.csrf.CsrfFilter : Did not protect against CSRF since request did not match And [CsrfNotRequired [TRACE, HEAD, GET, OPTIONS], Not [Or [org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda$1085/0x0000000801355c90#32e7df65]]]
2021-10-27 09:08:56.442 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking LogoutFilter (5/22)
2021-10-27 09:08:56.443 TRACE 10928 --- [nio-8888-exec-8] o.s.s.w.a.logout.LogoutFilter : Did not match request to Ant [pattern='/logout', POST]
2021-10-27 09:08:56.443 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking OAuth2AuthorizationEndpointFilter (6/22)
2021-10-27 09:08:56.444 TRACE 10928 --- [nio-8888-exec-8] o.s.s.authentication.ProviderManager : Authenticating request with OAuth2AuthorizationCodeRequestAuthenticationProvider (1/8)
2021-10-27 09:08:56.447 DEBUG 10928 --- [nio-8888-exec-8] o.s.s.web.DefaultRedirectStrategy : Redirecting to http://localhost:8080/authorized?code=iaFZSqcRLeJucw2mx_HNgji1PWN9QPHaUZt0htdH2zc3_4hEPFoBamnijwuRcK2xTzOT_W4jCTne3AmjAKB2gyoVzod5otPfgB8WSLc_8-x2B13oapwhlWX4dBUUER2e
2021-10-27 09:08:56.447 TRACE 10928 --- [nio-8888-exec-8] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match request to [Is Secure]
2021-10-27 09:08:56.447 DEBUG 10928 --- [nio-8888-exec-8] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2021-10-27 09:09:23.121 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda$1085/0x0000000801355c90#32e7df65, Filters=[org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#421d7900, org.springframework.security.web.context.SecurityContextPersistenceFilter#42a7e7e1, org.springframework.security.web.header.HeaderWriterFilter#55e88bc, org.springframework.security.web.csrf.CsrfFilter#20a116a0, org.springframework.security.web.authentication.logout.LogoutFilter#67e21ea2, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter#e2ee348, org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter#5d67bf4d, org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter#1477d4e6, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter#5ec3689b, org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter#448fa659, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#7c0a6f62, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter#4946dfde, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter#45964b9e, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#554e9509, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#34ea86ff, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#166a5659, org.springframework.security.web.session.SessionManagementFilter#4c12f54a, org.springframework.security.web.access.ExceptionTranslationFilter#417b3642, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#22ed2886, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter#76219fe, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter#4c599679, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter#1bcf2c64]] (1/2)
2021-10-27 09:09:23.122 DEBUG 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Securing POST /oauth2/token?grant_type=authorization_code&code=iaFZSqcRLeJucw2mx_HNgji1PWN9QPHaUZt0htdH2zc3_4hEPFoBamnijwuRcK2xTzOT_W4jCTne3AmjAKB2gyoVzod5otPfgB8WSLc_8-x2B13oapwhlWX4dBUUER2e&scope=dcube:write&redirect_uri=http://localhost:8888/authorized
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (1/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking SecurityContextPersistenceFilter (2/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] w.c.HttpSessionSecurityContextRepository : Created SecurityContextImpl [Null authentication]
2021-10-27 09:09:23.122 DEBUG 10928 --- [io-8888-exec-10] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (3/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking CsrfFilter (4/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.csrf.CsrfFilter : Did not protect against CSRF since request did not match And [CsrfNotRequired [TRACE, HEAD, GET, OPTIONS], Not [Or [org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda$1085/0x0000000801355c90#32e7df65]]]
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking LogoutFilter (5/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.s.w.a.logout.LogoutFilter : Did not match request to Ant [pattern='/logout', POST]
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking OAuth2AuthorizationEndpointFilter (6/22)
2021-10-27 09:09:23.123 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking OidcProviderConfigurationEndpointFilter (7/22)
2021-10-27 09:09:23.123 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking NimbusJwkSetEndpointFilter (8/22)
2021-10-27 09:09:23.123 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking OAuth2AuthorizationServerMetadataEndpointFilter (9/22)
2021-10-27 09:09:23.123 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking OAuth2ClientAuthenticationFilter (10/22)
2021-10-27 09:09:23.124 TRACE 10928 --- [io-8888-exec-10] o.s.s.authentication.ProviderManager : Authenticating request with OAuth2ClientAuthenticationProvider (1/8)
2021-10-27 09:09:23.124 WARN 10928 --- [io-8888-exec-10] o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt
2021-10-27 09:09:23.127 TRACE 10928 --- [io-8888-exec-10] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match request to [Is Secure]
2021-10-27 09:09:23.127 DEBUG 10928 --- [io-8888-exec-10] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2021-10-27 09:09:23.127 DEBUG 10928 --- [io-8888-exec-10] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2021-10-27 09:09:23.127 DEBUG 10928 --- [io-8888-exec-10] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
you need to delete following Bean from DefaultSecurityConfig class.
#Bean
UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user1")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
Add following method and an Autowired custom AuthenticationProvider to the same class
#Autowired
public void myCoolMethodName(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
Now everything working as I expected.
The complete DefaultSecurityConfig will be like
#EnableWebSecurity
public class DefaultSecurityConfig {
#Autowired
private CustomAuthenticationProvider authenticationProvider;
#Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated())
.formLogin(withDefaults());
return http.build();
}
#Autowired
public void whateverMethodName(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
First, cross-check that you've made relatable changes in the security config file.
#Configuration
#EnableWebSecurity
#ComponentScan("com.frugalis")
public class CustAuthProviderConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider authProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and().httpBasic();
}
}
The above code registers a custom authentication provider and authorizes users.
To create a custom user service, you need to implement the UserDetailsService interface and override the loadUserByUsername() method.
Create UserDetailsServiceImp class under service package.
public class UserDetailsServiceImp implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/*Here we are using dummy data, you need to load user data from
database or other third party application*/
User user = findUserbyUername(username);
UserBuilder builder = null;
if (user != null) {
builder = org.springframework.security.core.userdetails.User.withUsername(username);
builder.password(new BCryptPasswordEncoder().encode(user.getPassword()));
builder.roles(user.getRoles());
} else {
throw new UsernameNotFoundException("User not found.");
}
return builder.build();
}
private User findUserbyUername(String username) {
if(username.equalsIgnoreCase("admin")) {
return new User(username, "admin123", "ADMIN");
}
return null;
}
}
Make sure to use the appropriate spring boot version and maven repositories.
For more, refer to this example:Spring Security 5 - Custom UserDetailsService example
Now define a Custom Authentication Provider.
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
boolean shouldAuthenticateAgainstThirdPartySystem = true;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
if (name.equals("admin") && password.equals("password")) {
final List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
final UserDetails principal = new User(name, password, grantedAuths);
final Authentication auth = new UsernamePasswordAuthenticationToken(principal, password, grantedAuths);
return auth;
} else {
return null;
}
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
In the above code, we have retrieved the Username and password from the Authentication object. Once We retrieve the Authentication Object and Credentials, we are validating the username and password.
You can perform database-based authentication, we have done a hard-coded validation here.
Once the user is valid we try and set GRANTED_AUTHORITY in the list of String and return an UserDetails Object to the Caller an Authentication object. Instead of spring-provided UserDetails, we can customize the User object set in principal and return.
Custom Authentication Provider Spring Security

SAML 2.0 integration with Spring boot application issue

I am trying to integrate a Spring boot based application with and IDp that is using componentSpace lib for SAML.
The Spring application (service provider) working fine with other Idp like Octa. But while integrating with component Space it is facing issues and getting following error.
Differences when I compared the logs with Octa request:
Octa is sending Get request while from component space it is post request.
From Octa I am able to get userid (the login id) but from component space it is comping as anonymousUser.
So my question is can we hit Get request instead of Post. And any reason why it is not setting userId value?
Logs:
2020-12-16 07:00:55.800 TRACE 9144 --- [nio-8443-exec-1] o.s.security.web.FilterChainProxy : No security for POST /saml/sso
2020-12-16 07:00:55.800 TRACE 9144 --- [nio-8443-exec-1] o.s.security.web.FilterChainProxy : Invoking RequestCacheAwareFilter (8/13)
2020-12-16 07:00:55.800 TRACE 9144 --- [nio-8443-exec-1] o.s.s.w.s.HttpSessionRequestCache : No saved request
2020-12-16 07:00:55.800 TRACE 9144 --- [nio-8443-exec-1] o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderAwareRequestFilter (9/13)
2020-12-16 07:00:55.803 TRACE 9144 --- [nio-8443-exec-1] o.s.security.web.FilterChainProxy : Invoking AnonymousAuthenticationFilter (10/13)
2020-12-16 07:00:56.809 TRACE 9144 --- [nio-8443-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=10.9.109.194, SessionId=null], Granted Authorities=[ROLE_ANONYMOUS]]
2020-12-16 07:00:56.810 TRACE 9144 --- [nio-8443-exec-1] o.s.security.web.FilterChainProxy : Invoking SessionManagementFilter (11/13)
2020-12-16 07:00:56.810 TRACE 9144 --- [nio-8443-exec-1] o.s.security.web.FilterChainProxy : Invoking ExceptionTranslationFilter (12/13)
2020-12-16 07:00:56.810 TRACE 9144 --- [nio-8443-exec-1] o.s.security.web.FilterChainProxy : Invoking FilterSecurityInterceptor (13/13)
2020-12-16 07:00:56.811 TRACE 9144 --- [nio-8443-exec-1] edFilterInvocationSecurityMetadataSource : Did not match request to Ant [pattern='/saml**', OPTIONS] - [hasAnyRole('ROLE_')] (1/2)
2020-12-16 07:00:57.501 TRACE 9144 --- [nio-8443-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor : Did not re-authenticate AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=10.9.109.194, SessionId=null], Granted Authorities=[ROLE_ANONYMOUS]] before authorizing
2020-12-16 07:00:57.502 TRACE 9144 --- [nio-8443-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor : Authorizing filter invocation [POST /saml/sso] with attributes [authenticated]
2020-12-16 07:01:03.577 TRACE 9144 --- [nio-8443-exec-1] o.s.s.w.a.expression.WebExpressionVoter : Voted to deny authorization
2020-12-16 07:01:03.580 TRACE 9144 --- [nio-8443-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor : Failed to authorize filter invocation [POST /saml/sso] with attributes [authenticated] using AffirmativeBased [DecisionVoters=[org.springframework.security.web.access.expression.WebExpressionVoter#24cfcf19], AllowIfAllAbstainDecisions=false]
2020-12-16 07:01:03.709 TRACE 9144 --- [nio-8443-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'delegatingApplicationListener'
2020-12-16 07:01:03.709 TRACE 9144 --- [nio-8443-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'liveReloadServerEventListener'
2020-12-16 07:01:03.736 TRACE 9144 --- [nio-8443-exec-1] o.s.s.w.a.ExceptionTranslationFilter : Sending AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=10.9.109.194, SessionId=null], Granted Authorities=[ROLE_ANONYMOUS]] to authentication entry point since access is denied
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:73) ~[spring-security-core-5.4.1.jar:5.4.1]
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.attemptAuthorization(AbstractSecurityInterceptor.java:238) ~[spring-security-core-5.4.1.jar:5.4.1]
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:208) ~[spring-security-core-5.4.1.jar:5.4.1]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:113) ~[spring-security-web-5.4.1.jar:5.4.1]

Restricted Access Using Scope in JWT and Spring

Well, i have a JWT with scope definied in a claim called scp, see the peace of jwt:
"scp": "xpto_role user_impersonation",
So, in my Spring application i have the following Configuration:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/urlabc/**").hasAuthority("abc")
.antMatchers("/urlabc/**").authenticated();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("xpto");
}
}
ANd in my restController:
#RestController
public class TestResourceOne {
#RequestMapping(value = "/endpoint001")
public Double endpoint001(#RequestParam("value") Double value) {
return Math.sqrt(value);
}
}
Look, my scope passed by JWT is "xpto_role" in claim scp. In my SpringApp i want to force a Access Denied so i putted a "abc" role in "hasAuthority" method, but user is allowed to access my endpoint anyway.
The configuration is correct ?
Edit 1:
After remove the "authenticated()" line and putted the correct role i got Access Denied yet, see the error:
2018-07-20 16:57:36.732 DEBUG 6828 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /calcsqrt?value=10.0 at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2018-07-20 16:57:36.732 DEBUG 6828 --- [nio-8080-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter : SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.oauth2.provider.OAuth2Authentication#8f785707: Principal: null; Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=127.0.0.1, tokenType=BearertokenValue=<TOKEN>; Not granted any authorities'
2018-07-20 16:57:36.732 DEBUG 6828 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /calcsqrt?value=10.0 at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
2018-07-20 16:57:36.732 DEBUG 6828 --- [nio-8080-exec-1] s.CompositeSessionAuthenticationStrategy : Delegating to org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy#63dfada0
2018-07-20 16:57:36.732 DEBUG 6828 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /calcsqrt?value=10.0 at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2018-07-20 16:57:36.733 DEBUG 6828 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /calcsqrt?value=10.0 at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2018-07-20 16:57:36.733 DEBUG 6828 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/calcsqrt'; against '/calcsqrt/**'
2018-07-20 16:57:36.733 DEBUG 6828 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /calcsqrt?value=10.0; Attributes: [#oauth2.throwOnError(hasRole('ROLE_EXEC_CALCSQRT'))]
2018-07-20 16:57:36.733 DEBUG 6828 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.oauth2.provider.OAuth2Authentication#8f785707: Principal: null; Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=127.0.0.1, tokenType=BearertokenValue=<TOKEN>; Not granted any authorities
2018-07-20 16:57:36.733 DEBUG 6828 --- [nio-8080-exec-1] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter#7475b738, returned: -1
2018-07-20 16:57:36.734 DEBUG 6828 --- [nio-8080-exec-1] o.s.s.w.a.ExceptionTranslationFilter : Access is denied (user is not anonymous); delegating to AccessDeniedHandler
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233) ~[spring-security-core-4.2.7.RELEASE.jar:4.2.7.RELEASE]

Swagger2 ui not accessbile

I am using Swagger in a Spring boot application,
I somehow can access most of Swagger's endpoints such as /v2/api-docs, /swagger-resources but I can't figure out why /swagger-ui.html is not accessible.
I am using these dependencies:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
here is my Swagger Config class:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("app.controllers"))
.paths(PathSelectors.any())
.build();
}
}
Here is the interesting part of the log:
2017-12-27 14:12:09.896 DEBUG 10212 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /springfox/swagger-ui.html at position 12 of 13 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2017-12-27 14:12:09.896 DEBUG 10212 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /springfox/swagger-ui.html at position 13 of 13 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2017-12-27 14:12:09.897 DEBUG 10212 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/springfox/swagger-ui.html'; against '/'
2017-12-27 14:12:09.897 DEBUG 10212 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/springfox/swagger-ui.html'; against '/v2/api-docs'
2017-12-27 14:12:09.897 DEBUG 10212 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/springfox/swagger-ui.html'; against '/configuration/ui'
2017-12-27 14:12:09.897 DEBUG 10212 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/springfox/swagger-ui.html'; against '/swagger-resources'
2017-12-27 14:12:09.897 DEBUG 10212 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/springfox/swagger-ui.html'; against '/configuration/security'
2017-12-27 14:12:09.897 DEBUG 10212 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/springfox/swagger-ui.html'; against '/swagger-ui.html'
2017-12-27 14:12:09.897 DEBUG 10212 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/springfox/swagger-ui.html'; against '/webjars/**'
2017-12-27 14:12:09.897 DEBUG 10212 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'GET /springfox/swagger-ui.html' doesn't match 'POST /login
2017-12-27 14:12:09.897 DEBUG 10212 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /springfox/swagger-ui.html; Attributes: [authenticated]
2017-12-27 14:12:09.897 DEBUG 10212 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#8f3b828e: Principal: 0001; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_ADMIN, ROLE_USER
2017-12-27 14:12:09.903 DEBUG 10212 --- [nio-8080-exec-1] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter#45d0a23, returned: 1
2017-12-27 14:12:09.903 DEBUG 10212 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor : Authorization successful
2017-12-27 14:12:09.903 DEBUG 10212 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor : RunAsManager did not change Authentication object
2017-12-27 14:12:09.903 DEBUG 10212 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /springfox/swagger-ui.html reached end of additional filter chain; proceeding with original chain
2017-12-27 14:12:09.904 DEBUG 10212 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/springfox/swagger-ui.html]
2017-12-27 14:12:09.906 DEBUG 10212 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /springfox/swagger-ui.html
2017-12-27 14:12:09.919 DEBUG 10212 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported
2017-12-27 14:12:09.920 DEBUG 10212 --- [nio-8080-exec-1] .w.s.m.a.ResponseStatusExceptionResolver : Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported
2017-12-27 14:12:09.920 DEBUG 10212 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported
2017-12-27 14:12:09.920 WARN 10212 --- [nio-8080-exec-1] o.s.web.servlet.PageNotFound : Request method 'GET' not supported
2017-12-27 14:12:09.921 DEBUG 10212 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : SecurityContext 'org.springframework.security.core.context.SecurityContextImpl#8f3b828e: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#8f3b828e: Principal: 0001; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_ADMIN, ROLE_USER' stored to HttpSession: 'org.apache.catalina.session.StandardSessionFacade#3bcccd7c
2017-12-27 14:12:09.921 DEBUG 10212 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2017-12-27 14:12:09.921 DEBUG 10212 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Successfully completed request
2017-12-27 14:12:09.922 DEBUG 10212 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'delegatingApplicationListener'
2017-12-27 14:12:09.923 DEBUG 10212 --- [nio-8080-exec-1] o.s.s.w.a.ExceptionTranslationFilter : Chain processed normally
2017-12-27 14:12:09.923 DEBUG 10212 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2017-12-27 14:12:09.923 DEBUG 10212 --- [nio-8080-exec-1] o.s.b.w.f.OrderedRequestContextFilter : Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade#203209de
2017-12-27 14:12:09.923 DEBUG 10212 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost] : Processing ErrorPage[errorCode=0, location=/error]
2017-12-27 14:12:09.928 DEBUG 10212 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/error]
2017-12-27 14:12:09.928 DEBUG 10212 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /error
2017-12-27 14:12:09.930 DEBUG 10212 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity io.xhub.secusid.exception.SecusidErrorHandler.error(javax.servlet.http.HttpServletRequest)]
2017-12-27 14:12:09.930 DEBUG 10212 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'secusidErrorHandler'
2017-12-27 14:12:09.930 DEBUG 10212 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Last-Modified value for [/error] is: -1
2017-12-27 14:12:09.943 DEBUG 10212 --- [nio-8080-exec-1] i.x.s.exception.SecusidErrorHandler : Request method 'GET' not supported
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported
Try adding a class like this
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
// Make Swagger meta-data available via <baseURL>/v2/api-docs/
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
// Make Swagger UI available via <baseURL>/swagger-ui.html
registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/");
}
}

Custom ConstraintValidator is called even if user role not listed in #PreAuthorize

I have a Spring Boot app with POST endpoint, #PreAuthorize role definition and a custom validator for the request body:
#PreAuthorize("hasAnyRole('ROLE_OTHER')")
#RequestMapping(value = "post", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public String post(#RequestBody #Valid DummyDTO dummyDTO) {
...
}
1) When I call the endpoint with wrong role and non-valid request body the custom validation code i.e. ConstraintValidator.isValid() is executed and I got 400 Bad Request.
2) When I call the endpoint with wrong role and valid request body the custom validation code i.e. ConstraintValidator.isValid() is executed and I got 403 Forbidden.
Why is the custom validation code executed before the #PreAuthorize check? I would expect to get 403 in both cases.
DEBUG 12776 --- [nio-8080-exec-3] o.s.s.w.a.i.FilterSecurityInterceptor : Successfully Authenticated: org.springframework.security.authentication.TestingAuthenticationToken#bbd5887f: Principal: key; Credentials: [PROTECTED]; Authenticated: false; Details: null; Granted Authorities: ROLE_USER
DEBUG 12776 --- [nio-8080-exec-3] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter#41ba74ef, returned: 1
DEBUG 12776 --- [nio-8080-exec-3] o.s.s.w.a.i.FilterSecurityInterceptor : Authorization successful
DEBUG 12776 --- [nio-8080-exec-3] o.s.s.w.a.i.FilterSecurityInterceptor : RunAsManager did not change Authentication object
DEBUG 12776 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy : /post reached end of additional filter chain; proceeding with original chain
INFO 12776 --- [nio-8080-exec-3] com.example.DummyValidator : > isValid(): [valid]
DEBUG 12776 --- [nio-8080-exec-3] o.s.s.a.i.a.MethodSecurityInterceptor : Secure object: ReflectiveMethodInvocation: public java.lang.String com.example.MyController.post(com.example.DummyDTO); target is of class [com.example.MyController]; Attributes: [[authorize: 'hasAnyRole('ROLE_OTHER')', filter: 'null', filterTarget: 'null']]
DEBUG 12776 --- [nio-8080-exec-3] o.s.s.authentication.ProviderManager : Authentication attempt using org.springframework.security.authentication.TestingAuthenticationProvider
DEBUG 12776 --- [nio-8080-exec-3] o.s.s.a.i.a.MethodSecurityInterceptor : Successfully Authenticated: org.springframework.security.authentication.TestingAuthenticationToken#bbd5887f: Principal: key; Credentials: [PROTECTED]; Authenticated: false; Details: null; Granted Authorities: ROLE_USER
DEBUG 12776 --- [nio-8080-exec-3] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter#356a19a2, returned: -1
DEBUG 12776 --- [nio-8080-exec-3] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.access.vote.RoleVoter#7290b3e0, returned: 0
DEBUG 12776 --- [nio-8080-exec-3] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.access.vote.AuthenticatedVoter#16f3e525, returned: 0
DEBUG 12776 --- [nio-8080-exec-3] o.s.s.w.a.ExceptionTranslationFilter : Access is denied (user is not anonymous); delegating to AccessDeniedHandler
org.springframework.security.access.AccessDeniedException: Access is denied
...
My DTO:
public class DummyDTO {
#DummyConstraint
private String name;
...
}
constraint:
#Documented
#Retention(RUNTIME)
#Target({TYPE, FIELD, PARAMETER, ANNOTATION_TYPE})
#Constraint(validatedBy = {DummyValidator.class})
public #interface DummyConstraint {
...
}
and validator:
public class DummyValidator implements ConstraintValidator<DummyConstraint, String> {
private static final Logger LOGGER = LoggerFactory.getLogger(DummyValidator.class);
#Override
public void initialize(DummyConstraint constraintAnnotation) {
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
LOGGER.info("> isValid(): [{}]", value);
return "valid".equalsIgnoreCase(value);
}
}

Resources