springboot 3.0.2 spring security 6.0.1 I wang to custom a LoginFilter to replace UsernamePasswordAuthenticationFilter. but It dosn't work, even the LoginFilter is the same as the UsernamePasswordAuthenticationFilter. When I try to get user infomation in controller by SecurityContextHolder.getContext().getAuthentication().getPrincipal(), it always is a anonymous user.When I disable anonymous user, it becomes null. It only work when I put the custom Filter after UsernamePasswordAuthenticationFilter. But the code of custom Filter don't run. If I put custom Filter Here is my configuration and Login Filter:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
@Autowired
UserService userService;
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
AuthenticationManager authenticationManager(HttpSecurity httpSecurity) throws Exception {
AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
.userDetailsService(userService)
.passwordEncoder(passwordEncoder())
.and()
.build();
return authenticationManager;
}
@Bean
public SecurityFilterChain securityFilterChain(AuthenticationManager authenticationManager, HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.authenticationManager(authenticationManager)
.addFilterAfter(new LoginFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.accessDeniedPage("/page/login_register")
.and()
.formLogin()
.loginProcessingUrl("/login")
.loginPage("/page/login_register")
.and()
.csrf()
.disable()
.build();
}
}
public class LoginFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login",
"POST");
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private boolean postOnly = true;
public LoginFilter() {
super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
}
public LoginFilter(AuthenticationManager authenticationManager) {
super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
username = (username != null) ? username.trim() : "";
String password = obtainPassword(request);
password = (password != null) ? password : "";
UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
@Nullable
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(this.passwordParameter);
}
@Nullable
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(this.usernameParameter);
}
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
}
public void setUsernameParameter(String usernameParameter) {
Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
this.usernameParameter = usernameParameter;
}
public void setPasswordParameter(String passwordParameter) {
Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
this.passwordParameter = passwordParameter;
}
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
public final String getUsernameParameter() {
return this.usernameParameter;
}
public final String getPasswordParameter() {
return this.passwordParameter;
}
}
Comment From: marcusdacoregio
Hi @NebulaXIX,
The UsernamePasswordAuthenticationFilter is usually set up by FormLoginConfigurer which does a few things when creating the filter. One of them is getting the SecurityContextRepository from the SecurityContextConfigurer, and setting it into the UsernamePasswordAuthenticationFilter, replacing the default RequestAttributeSecurityContextRepository.
That said, with the information that you provided, I assume that you have to set the proper SecurityContextRepository for it to work, try doing the following:
public LoginFilter(AuthenticationManager authenticationManager) {
super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
setSecurityContextRepository(new DelegatingSecurityContextRepository(
new RequestAttributeSecurityContextRepository(), new HttpSessionSecurityContextRepository());)
}