1

After logging in successfully via POST /api/v1/auth/login, subsequent requests to GET /api/v1/userAccounts/me are considered to be anonymous according to Spring Security.

This is my security config:

@Configuration
@EnableGlobalMethodSecurity(
    prePostEnabled = true,
    securedEnabled = true,
    jsr250Enabled = true
)
@Order(1)
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private final UserDetailsServiceImpl userDetailsService;
    private final Config config;
    private final ObjectMapper objectMapper;

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

            .csrf()
            .disable()

            .authorizeRequests()
            .antMatchers(
                "/api/v1/auth/**",
                "/api/v1/reference/**"
            )
            .permitAll()
            .and()

            .authorizeRequests()
            .anyRequest()
            .authenticated()
            .and()

            .formLogin()
            .successHandler((request, response, authentication) -> {
                response.getWriter().append("OK");
                response.setStatus(HttpServletResponse.SC_OK);
            })
            .failureHandler((request, response, exception) -> {
                response.getWriter().append("Invalid credentials or inactive account");
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            })
            .loginProcessingUrl("/api/v1/auth/login")
            .permitAll()
            .and()

            .logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/api/v1/auth/logout", "POST"))
            .permitAll()
            .and()

            .exceptionHandling()
            .accessDeniedHandler((request, response, accessDeniedException) -> {
                response.setContentType(MediaType.APPLICATION_JSON_VALUE);
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                objectMapper.writeValue(
                    response.getWriter(),
                    ErrorResponseBody
                        .builder()
                        .code(ErrorType.ACCESS_DENIED)
                        .status(HttpServletResponse.SC_FORBIDDEN)
                        .message("Access denied")
                        .build()
                );
            })
            .authenticationEntryPoint((request, response, authException) -> {
                response.setContentType(MediaType.APPLICATION_JSON_VALUE);
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                objectMapper.writeValue(
                    response.getWriter(),
                    ErrorResponseBody
                        .builder()
                        .code(ErrorType.LOGIN_REQUIRED)
                        .status(HttpServletResponse.SC_UNAUTHORIZED)
                        .message("You are not authorized to access this resource")
                        .build()
                );
            })
            .and()

            .userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        final var configuration = new CorsConfiguration();
        configuration.setAllowCredentials(true);
        configuration.setAllowedOrigins(config.getAllowedOrigins());
        configuration.setAllowedMethods(asList("GET", "POST", "PUT", "PATCH", "DELETE"));
        configuration.setAllowedHeaders(Arrays.asList(HttpHeaders.AUTHORIZATION, HttpHeaders.CACHE_CONTROL, HttpHeaders.CONTENT_TYPE, HttpHeaders.ACCEPT));
        final var source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/api/**", configuration);
        return source;
    }
}

Here's the debug logging from the form login request:

2020-11-25 22:41:27.017 DEBUG 74979 --- [nio-8080-exec-9] o.s.security.web.FilterChainProxy        : /api/v1/auth/login at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-11-25 22:41:27.017 DEBUG 74979 --- [nio-8080-exec-9] o.s.security.web.FilterChainProxy        : /api/v1/auth/login at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-11-25 22:41:27.017 DEBUG 74979 --- [nio-8080-exec-9] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2020-11-25 22:41:27.017 DEBUG 74979 --- [nio-8080-exec-9] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2020-11-25 22:41:27.018 DEBUG 74979 --- [nio-8080-exec-9] o.s.security.web.FilterChainProxy        : /api/v1/auth/login at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-11-25 22:41:27.018 DEBUG 74979 --- [nio-8080-exec-9] o.s.security.web.FilterChainProxy        : /api/v1/auth/login at position 4 of 12 in additional filter chain; firing Filter: 'CorsFilter'
2020-11-25 22:41:27.018 DEBUG 74979 --- [nio-8080-exec-9] o.s.security.web.FilterChainProxy        : /api/v1/auth/login at position 5 of 12 in additional filter chain; firing Filter: 'LogoutFilter'
2020-11-25 22:41:27.018 DEBUG 74979 --- [nio-8080-exec-9] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/v1/auth/login'; against '/api/v1/auth/logout'
2020-11-25 22:41:27.018 DEBUG 74979 --- [nio-8080-exec-9] o.s.security.web.FilterChainProxy        : /api/v1/auth/login at position 6 of 12 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2020-11-25 22:41:27.018 DEBUG 74979 --- [nio-8080-exec-9] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/v1/auth/login'; against '/api/v1/auth/login'
2020-11-25 22:41:27.018 DEBUG 74979 --- [nio-8080-exec-9] w.a.UsernamePasswordAuthenticationFilter : Request is to process authentication
2020-11-25 22:41:27.018 DEBUG 74979 --- [nio-8080-exec-9] o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2020-11-25 22:41:27.145 DEBUG 74979 --- [nio-8080-exec-9] s.CompositeSessionAuthenticationStrategy : Delegating to org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy@454043bf
2020-11-25 22:41:27.145 DEBUG 74979 --- [nio-8080-exec-9] w.a.UsernamePasswordAuthenticationFilter : Authentication success. Updating SecurityContextHolder to contain: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@1fcaf72e: Principal: com.example.server.security.UserDetailsServiceImpl$UserDetailsImpl@3bd8fbc9; 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: ONBOARD
2020-11-25 22:41:27.145 DEBUG 74979 --- [nio-8080-exec-9] 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@2e14ccb
2020-11-25 22:41:27.145 DEBUG 74979 --- [nio-8080-exec-9] w.c.HttpSessionSecurityContextRepository : HttpSession being created as SecurityContext is non-default
2020-11-25 22:41:27.145 DEBUG 74979 --- [nio-8080-exec-9] w.c.HttpSessionSecurityContextRepository : SecurityContext 'org.springframework.security.core.context.SecurityContextImpl@1fcaf72e: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@1fcaf72e: Principal: com.example.server.security.UserDetailsServiceImpl$UserDetailsImpl@3bd8fbc9; 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: ONBOARD' stored to HttpSession: 'org.apache.catalina.session.StandardSessionFacade@481bcf3f
2020-11-25 22:41:27.145 DEBUG 74979 --- [nio-8080-exec-9] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

and the logs from the GET /api/v1/userAccounts/me request which follows:

2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] o.s.security.web.FilterChainProxy        : /api/v1/userAccounts/me at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] o.s.security.web.FilterChainProxy        : /api/v1/userAccounts/me at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] w.c.HttpSessionSecurityContextRepository : HttpSession returned null object for SPRING_SECURITY_CONTEXT
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@6384c25. A new one will be created.
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] o.s.security.web.FilterChainProxy        : /api/v1/userAccounts/me at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] o.s.security.web.FilterChainProxy        : /api/v1/userAccounts/me at position 4 of 12 in additional filter chain; firing Filter: 'CorsFilter'
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] o.s.security.web.FilterChainProxy        : /api/v1/userAccounts/me at position 5 of 12 in additional filter chain; firing Filter: 'LogoutFilter'
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /api/v1/userAccounts/me' doesn't match 'POST /api/v1/auth/logout'
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] o.s.security.web.FilterChainProxy        : /api/v1/userAccounts/me at position 6 of 12 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /api/v1/userAccounts/me' doesn't match 'POST /api/v1/auth/login'
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] o.s.security.web.FilterChainProxy        : /api/v1/userAccounts/me at position 7 of 12 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.s.DefaultSavedRequest            : pathInfo: both null (property equals)
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.s.DefaultSavedRequest            : queryString: both null (property equals)
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.s.DefaultSavedRequest            : requestURI: arg1=/api/v1/userAccounts/me; arg2=/api/v1/userAccounts/me (property equals)
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.s.DefaultSavedRequest            : serverPort: arg1=8080; arg2=8080 (property equals)
2020-11-25 22:41:27.198 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.s.DefaultSavedRequest            : requestURL: arg1=http://localhost:8080/api/v1/userAccounts/me; arg2=http://localhost:8080/api/v1/userAccounts/me (property equals)
2020-11-25 22:41:27.199 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.s.DefaultSavedRequest            : scheme: arg1=http; arg2=http (property equals)
2020-11-25 22:41:27.199 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.s.DefaultSavedRequest            : serverName: arg1=localhost; arg2=localhost (property equals)
2020-11-25 22:41:27.199 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.s.DefaultSavedRequest            : contextPath: arg1=; arg2= (property equals)
2020-11-25 22:41:27.199 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.s.DefaultSavedRequest            : servletPath: arg1=/api/v1/userAccounts/me; arg2=/api/v1/userAccounts/me (property equals)
2020-11-25 22:41:27.199 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.s.HttpSessionRequestCache        : Removing DefaultSavedRequest from session if present
2020-11-25 22:41:27.199 DEBUG 74979 --- [io-8080-exec-10] o.s.security.web.FilterChainProxy        : /api/v1/userAccounts/me at position 8 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2020-11-25 22:41:27.199 DEBUG 74979 --- [io-8080-exec-10] o.s.security.web.FilterChainProxy        : /api/v1/userAccounts/me at position 9 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2020-11-25 22:41:27.199 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.a.AnonymousAuthenticationFilter  : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@4c26704b: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 4ED2DAB934C3163D40A9FE32EFE26A2A; Granted Authorities: ROLE_ANONYMOUS'
2020-11-25 22:41:27.199 DEBUG 74979 --- [io-8080-exec-10] o.s.security.web.FilterChainProxy        : /api/v1/userAccounts/me at position 10 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter'
2020-11-25 22:41:27.199 DEBUG 74979 --- [io-8080-exec-10] o.s.security.web.FilterChainProxy        : /api/v1/userAccounts/me at position 11 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2020-11-25 22:41:27.199 DEBUG 74979 --- [io-8080-exec-10] o.s.security.web.FilterChainProxy        : /api/v1/userAccounts/me at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2020-11-25 22:41:27.200 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /api/v1/userAccounts/me' doesn't match 'POST /api/v1/auth/logout'
2020-11-25 22:41:27.200 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/v1/userAccounts/me'; against '/api/v1/auth/**'
2020-11-25 22:41:27.200 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/v1/userAccounts/me'; against '/api/v1/reference/**'
2020-11-25 22:41:27.200 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /api/v1/userAccounts/me; Attributes: [authenticated]
2020-11-25 22:41:27.200 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@4c26704b: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 4ED2DAB934C3163D40A9FE32EFE26A2A; Granted Authorities: ROLE_ANONYMOUS
2020-11-25 22:41:27.200 DEBUG 74979 --- [io-8080-exec-10] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@1970e63, returned: -1
2020-11-25 22:41:27.201 DEBUG 74979 --- [io-8080-exec-10] o.s.s.w.a.ExceptionTranslationFilter     : Access is denied (user is anonymous); redirecting to authentication entry point

Any idea why the request is being considered anonymous?

Steve
  • 8,066
  • 11
  • 70
  • 112
  • It seems that you are not updating the `SecurityContext`. This should help https://stackoverflow.com/questions/64427881/spring-security-401-unauthorized-access/64430168#64430168 – Aman Nov 25 '20 at 12:22
  • I have no idea what a `SecurityContext` is or how, when and/or where it should be updated. – Steve Nov 25 '20 at 12:25
  • You aren't sending the session cookie, hence no way to retrieve the existing SecurityContext, hence anonymous. – M. Deinum Nov 25 '20 at 12:33
  • Not sure what you mean There's a `JSESSIONID` cookie. Is that the cookie being used by Spring Security or is it something else? Please bear in mind that I'm not a Spring Security enthusiast let alone an expert. I don't understand the complex concepts that seem to underpin something so basic as logging in. I'm using Spring Security so I don't have to manually check for a valid session in every controller endpoint. Care to post an answer? – Steve Nov 25 '20 at 12:38
  • @Distortum my previous comment was for non-form login context, as **M.Deinum** mentioned, it a session problem in this case. there is a good example including how to do logout here https://www.innoq.com/en/blog/cookie-based-spring-security-session/ – Aman Nov 25 '20 at 13:27
  • I fallen in a similar behavior, in my case was due to multiple instances of `JSESSIONID` in the cookies, I needed to change the application cookie name in `web.xml`. See this question for details: https://stackoverflow.com/a/76923042/2186777 – fl4l Aug 17 '23 at 21:31

0 Answers0