0
votes

I have a Restful APIs developed using Springboot. Also, I have implementioned external ldap authentication. http://localhost:8080/login?username=test&password=test goes through the ldap authentication and it is working fine as expected.

My Spring security implementation is below. I am restricting all the API calls and needs to be authenticated.

@Configuration
@EnableWebSecurity
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
        .csrf().disable()
        .authorizeRequests()
        .antMatchers("/login").permitAll()
        .anyRequest().authenticated()        
        .and().sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    }
...
...
}

My login request works and all the API calls are not getting authenticated, even after the successful login. I can see the logs show that ldap authentication is success and creates the SecurityContext. And my next API call does not have this SecurityContext created in the previous login request. My logs are below.

2019-10-11 02:22:31.567 DEBUG 39216 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /login?username=test&password=test at position 5 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter' 2019-10-11 02:22:31.567 DEBUG 39216 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/login'; against '/login' 2019-10-11 02:22:31.567 DEBUG 39216 --- [nio-8080-exec-1] w.a.UsernamePasswordAuthenticationFilter : Request is to process authentication 2019-10-11 02:22:31.570 DEBUG 39216 --- [nio-8080-exec-1] o.s.s.authentication.ProviderManager : Authentication attempt using portal.services.security.CustomActiveDirectoryLdapAuthenticationProvider 2019-10-11 02:22:32.767 DEBUG 39216 --- [nio-8080-exec-1] o.s.s.ldap.SpringSecurityLdapTemplate : Searching for entry under DN '', base = 'dc=abc,dc=com', filter = '(&(objectClass=Person) ((sAMAccountName=12323)))' 2019-10-11 02:22:32.777 DEBUG 39216 --- [nio-8080-exec-1] o.s.s.ldap.SpringSecurityLdapTemplate : Found DN: CN=Test Name,OU=Users,DC=com 2019-10-11 02:22:32.779 INFO 39216 --- [nio-8080-exec-1] o.s.s.ldap.SpringSecurityLdapTemplate : Ignoring PartialResultException 2019-10-11 02:22:32.896 DEBUG 39216 --- [nio-8080-exec-1] o.s.s.l.u.LdapUserDetailsMapper : Mapping user details from context with DN: CN=test name,OU=Users,DC=com 2019-10-11 02:22:32.899 DEBUG 39216 --- [nio-8080-exec-1] s.CompositeSessionAuthenticationStrategy : Delegating to org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy@45a4b042 2019-10-11 02:22:32.899 DEBUG 39216 --- [nio-8080-exec-1] w.a.UsernamePasswordAuthenticationFilter : Authentication success. Updating SecurityContextHolder to contain: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@ff0ce9d1: Principal: org.springframework.security.ldap.userdetails.LdapUserDetailsImpl@7a480d2: Dn: CN=Test name,OU=Users,DC=com; Username: test; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; CredentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ADMIN; Credentials: [PROTECTED]; Authenticated: true; Details: Users; Granted Authorities: ADMIN 2019-10-11 02:22:33.117 DEBUG 39216 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@17741df1 2019-10-11 02:22:33.117 DEBUG 39216 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed 2019-10-11 02:22:33.650 DEBUG 39216 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /admin/getCutOffDays at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter' 2019-10-11 02:22:33.650 DEBUG 39216 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /admin/getCutOffDays at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter' 2019-10-11 02:22:33.654 DEBUG 39216 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /admin/getCutOffDays at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter' 2019-10-11 02:22:33.654 DEBUG 39216 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /admin/getCutOffDays at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter' 2019-10-11 02:22:33.654 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', GET] 2019-10-11 02:22:33.655 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'OPTIONS /admin/getCutOffDays' doesn't match 'GET /logout 2019-10-11 02:22:33.655 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', POST] 2019-10-11 02:22:33.655 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'OPTIONS /admin/getCutOffDays' doesn't match 'POST /logout 2019-10-11 02:22:33.655 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', PUT] 2019-10-11 02:22:33.655 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'OPTIONS /admin/getCutOffDays' doesn't match 'PUT /logout 2019-10-11 02:22:33.656 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', DELETE] 2019-10-11 02:22:33.656 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'OPTIONS /admin/getCutOffDays' doesn't match 'DELETE /logout 2019-10-11 02:22:33.656 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.web.util.matcher.OrRequestMatcher : No matches found 2019-10-11 02:22:33.657 DEBUG 39216 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /admin/getCutOffDays at position 5 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter' 2019-10-11 02:22:33.657 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'OPTIONS /admin/getCutOffDays' doesn't match 'POST /login 2019-10-11 02:22:33.657 DEBUG 39216 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /admin/getCutOffDays at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter' 2019-10-11 02:22:33.657 DEBUG 39216 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /admin/getCutOffDays at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter' 2019-10-11 02:22:33.659 DEBUG 39216 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /admin/getCutOffDays at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter' 2019-10-11 02:22:33.660 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@25109219: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS' 2019-10-11 02:22:33.661 DEBUG 39216 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /admin/getCutOffDays at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter' 2019-10-11 02:22:33.662 DEBUG 39216 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /admin/getCutOffDays at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter' 2019-10-11 02:22:33.671 DEBUG 39216 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy
: /admin/getCutOffDays at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor' 2019-10-11 02:22:33.673 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/admin/getCutOffDays'; against '/login' 2019-10-11 02:22:33.674 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /admin/getCutOffDays; Attributes: [authenticated] 2019-10-11 02:22:33.674 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@25109219: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS 2019-10-11 02:22:33.688 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@74f28afc, returned: -1 2019-10-11 02:22:33.696 DEBUG 39216 --- [nio-8080-exec-2] o.s.s.w.a.ExceptionTranslationFilter : Access is denied (user is anonymous); redirecting to authentication entry point

org.springframework.security.access.AccessDeniedException: Access is denied at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-5.0.8.RELEASE.jar!/:5.0.8.RELEASE] at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233) ~[spring-security-core-5.0.8.RELEASE.jar!/:5.0.8.RELEASE] at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:124) ~[spring-security-web-5.0.8.RELEASE.jar!/:5.0.8.RELEASE] at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) ~[spring-security-web-5.0.8.RELEASE.jar!/:5.0.8.RELEASE]

I am expecting my request to /admin/getCutOffDays should retrieve the data as it is authenticated in the previous login request.

Below is my Controller.

@RequestMapping("/admin")
@RestController
public class AdminController {
    @PreAuthorize("hasAuthority('ADMIN')")
    @GetMapping("/getCutOffDays")
    public long getCutOffDays() {
        return adminService.getSequenceId(CUT_OFF_DAYS);
    }
}

Can anyone help me figure out what am I missing here.

1
Hi, Passing the password in the URL is not a very good idea. I would use a POST instead, so that the password gets sent in the body of the request. Can you give more details about how you implemented the LDAP authentication?Peter Catalin
As you are marking session policy as SessionCreationPolicy.STATELESS. The spring will not save session for next request.You will need to pass token like JWT token in next requests and create SecurityContextRepository to save sesssions.Jaspreet Jolly
For more understanding, use dzone.com/articles/… to learn how JWT can be implemented.Jaspreet Jolly

1 Answers

0
votes

To answer your question correctly, I would need to have some more input from your side. I'll try to answer it by making some assumptions.

I'm assuming you're authenticating your application and then making a REST call afterward. First (as already mentioned in the answers), you have a STATELESS session policy. It means the session won't be available for the subsequent Http requests.

You can handle it by:

  1. Implementing a token-based (transparent/opaque) authentication.
  2. Implementing a basic-authentication.

In both cases, every request to your REST APIs requires an Authorization Header (or a Cookie, depends on the approach).