Describe the bug Security Context is not loaded in Session after authentication is performed

To Reproduce User authentication was successfully performed and page redirection was performed using Custom UsernamePasswordAuthenticationFilter. Subsequently, we tried to obtain the principal information to obtain authentic user information at the controller layer (using SecurityContextHolder or Princial as a parameter) but there was a problem returning null in any way, and the only way I could solve it was to add the work of putting SecuryContext directly in the session at SuccessHandler. But it doesn't seem normal.

Expected behavior I inherited the "UsernamePasswordAuthenticationFilter" class and the "AuthenticationProvider" class to carry out my newly created authentication and register the Success Handler to re-direct the page.

At this time, the securityContext contained in the SecurityContextHolder from filter to accessHandler was all returned to the same object, which is thought to have been registered normally. And internally, I understand that SecurityContext is registered in the SecurityContextPersistenceFilter in the session.

However, contrary to my idea, SecurityContext did not exist when I performed a redirect using a handler and checked the session on my controller.

So I added a code from the SuccessHandler and solved it by putting SecurityContext directly into the session, but I don't think that's the recommended way.

  • The code when I added to include SecurityContext in my session are as follows.
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {   
        **/* Setting Security Context to Session. */**
        request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
                                                            SecurityContextHolder.getContext());



            /*redirect */
        if(authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_ADMIN"))) {
            getRedirectStrategy().sendRedirect(request, response, "admin");
        }else {
            getRedirectStrategy().sendRedirect(request, response, "home");
        }

    }

The methods in which I tried to get Context, principal from the controller but all returned null are as follows:

1.@AuthenticationPrincipal 2. authentication.getPrincipal() 3. SecurityContextHolder.getContext().getAuthenation();

I solved this problem by using the method of registering SecurityContext directly in the session, but do you know why this issue happened?

My Code

- Security configuration

@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    UserDetailsService loginService;
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**").anyRequest(); // resources 이후 디렉토리에대한 접근권한 허가
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
         http.authorizeRequests()
         .antMatchers("/home/**").authenticated()
         .antMatchers("/admin/**").authenticated()
         .antMatchers("/**").permitAll();

     http.formLogin()
             .loginPage("/")
             .loginProcessingUrl("/login")
             .defaultSuccessUrl("/home")
             .permitAll();

     http.logout()
             .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
             .logoutSuccessUrl("/login")
             .invalidateHttpSession(true);

     http.exceptionHandling()
             .accessDeniedPage("/denied");
        }

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

    @Bean
    public CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
        CustomAuthenticationFilter customAuthenticationFilter = new CustomAuthenticationFilter(authenticationManager());
        customAuthenticationFilter.setFilterProcessesUrl("/login");
        customAuthenticationFilter.setAuthenticationSuccessHandler(customLoginSuccessHandler());
        customAuthenticationFilter.setAuthenticationFailureHandler(curstomAuthenticationFailureHandler());
        customAuthenticationFilter.setAllowSessionCreation(true);
        customAuthenticationFilter.afterPropertiesSet();
        return customAuthenticationFilter;
    }

    @Bean
    public AuthenticationFailureHandler curstomAuthenticationFailureHandler() {
        CustomAuthenticationFailureHandler failureHandler = new CustomAuthenticationFailureHandler();
        failureHandler.setDefaultFailureUrl("/?error=error");
        return failureHandler;
    }

    @Bean
    public CustomLoginSuccessHandler customLoginSuccessHandler() {
        return new CustomLoginSuccessHandler();
    }

    @Bean
    public CustomAuthenticationProvider customAuthenticationProvider() {
        return new CustomAuthenticationProvider(new BCryptPasswordEncoder());
    }
}

- CustomAuthenticationFilter.java

public class CustomAuthenticationFilter  extends UsernamePasswordAuthenticationFilter{
    public CustomAuthenticationFilter(AuthenticationManager authenticationManager) {
        super.setAuthenticationManager(authenticationManager);
    }
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(request.getParameter("username"), request.getParameter("password")); 
        setDetails(request, token);
        return this.getAuthenticationManager().authenticate(token);
    } 
}

- CustomAuthenticationProvider.java

@RequiredArgsConstructor
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private UserDetailsService loginService;

    @NonNull
    private BCryptPasswordEncoder passwordEncoder;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
        String userEmail = (String) token.getName();
        String userPassword=(String) token.getCredentials();
        UserDetails member=(Member)loginService.loadUserByUsername(userEmail);
        if (!passwordEncoder.matches(userPassword, member.getPassword())) {
            throw new BadCredentialsException(member.getUsername() + "Invalid password");
        }
        return new UsernamePasswordAuthenticationToken(member.getUsername(),member.getPassword(),member.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}

**- CustomLoginSuccessHandler .java **

public class CustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {   
        if(authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_ADMIN"))) {
            getRedirectStrategy().sendRedirect(request, response, "admin");
        }else {
            getRedirectStrategy().sendRedirect(request, response, "home");
        }

}

- Controller -

@Controller
public class HomeController {
    @GetMapping("/admin")
    public String admin(HttpSession session,@RequestParam Map<String,Object> model) {
        System.out.println(session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY));
        return "admin";
    }

}

Comment From: rwinch

Thanks for getting in touch, but it feels like this is a question that would be better suited to Stack Overflow. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it) or add some more details if you feel this is a genuine bug.