My custom filter based on UsernamePasswordAuthenticationFilter
needs an AuthenticationManager
but every time I call the method attemptAuthentication()
the compilation fails here:
Authentication auth2 = this.getAuthenticationManager().authenticate(authRequest);
The AuthenticationManager
is null :
java.lang.NullPointerException: null
at app.shellx.security.CustomUsernamePasswordAuthenticationFilter.attemptAuthentication(CustomUsernamePasswordAuthenticationFilter.java:75) ~[classes/:na]
WebSecurityConfig
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
@Autowired
private JwtTokenFilter jwtTokenFilter;
@Autowired
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.cors().and()
.csrf().disable()
.authorizeRequests() // .antMatchers("/**")
.antMatchers("/login/**", "/register/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
//.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
.addFilterAt(new CustomUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.formLogin()
.loginPage("http://localhost:4200/login")//.failureUrl("/login-error")
.loginProcessingUrl("/login")
.usernameParameter("email")
.successHandler(customAuthenticationSuccessHandler)
.and()
.logout()
.permitAll();
}
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider()); // AuthenticationProvider inserted into ProviderManager
}
@Bean
public CustomDaoAuthenticationProvider authenticationProvider() {
CustomDaoAuthenticationProvider authenticationProvider = new CustomDaoAuthenticationProvider();
authenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
authenticationProvider.setUserDetailsService(userService);
return authenticationProvider;
}
/*@Bean
public CustomUsernamePasswordAuthenticationFilter customUsernamePasswordAuthenticationFilter() throws Exception {
return new CustomUsernamePasswordAuthenticationFilter(authenticationManager());
}*/
//@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
// Return the AuthenticationManager used by the configure(AuthenticationManagerBuilder auth) method
@Bean(name = "CustomAuthenticationManager")
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
System.out.println("Configuration of authenticationManagerBean");
return super.authenticationManagerBean();
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebConfig() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins(
"http://localhost:4200")
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS")
.allowedHeaders("Content-Type", "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method",
"Access-Control-Request-Headers", "Authorization", "Cache-Control",
"Access-Control-Allow-Origin")
.exposedHeaders("Access-Control-Allow-Origin", "Access-Control-Allow-Credentials")
.allowCredentials(true).maxAge(3600);
}
};
}
}
Custom Filter
public class CustomUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private boolean postOnly = true;
public CustomUsernamePasswordAuthenticationFilter() { // AuthenticationManager authenticationManager
super(new AntPathRequestMatcher("/login", "POST"));
//this.setAuthenticationManager(authenticationManager);
System.out.println("Constructor filter");
}
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
UsernamePasswordAuthenticationToken authRequest = null;
try {
authRequest = this.getUserNamePasswordAuthenticationToken(request);
} catch (IOException e) {
e.printStackTrace();
}
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
System.out.println("AVANT AUTHENTIFICATION : "+authRequest.getPrincipal() + " " + authRequest.getCredentials());
//this.setAuthenticationManager(authenticationManager);
Authentication auth2 = this.getAuthenticationManager().authenticate(authRequest);
System.out.println("APRES AUTHENTIFICATION : "+auth2.getPrincipal() + " " + auth2.getCredentials());
return auth2;
}
private UsernamePasswordAuthenticationToken getUserNamePasswordAuthenticationToken(HttpServletRequest request) throws IOException {
StringBuffer sb = new StringBuffer();
BufferedReader bufferedReader = null;
String content = "";
AuthReq sr = null;
try {
bufferedReader = request.getReader();
char[] charBuffer = new char[128];
int bytesRead;
while ( (bytesRead = bufferedReader.read(charBuffer)) != -1 ) {
sb.append(charBuffer, 0, bytesRead);
}
content = sb.toString();
ObjectMapper objectMapper = new ObjectMapper();
try{
sr = objectMapper.readValue(content, AuthReq.class);
}catch(Throwable t){
throw new IOException(t.getMessage(), t);
}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
System.out.println("email : "+sr.getEmail());
System.out.println("password : "+sr.getPassword());
return new UsernamePasswordAuthenticationToken(sr.getEmail(), sr.getPassword());
}
public static class AuthReq {
String email;
String password;
public String getEmail() {
return email;
}
public String getPassword() {
return password;
}
}
@Autowired
@Qualifier("CustomAuthenticationManager")
@Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
System.out.println("Setter custom filter");
super.setAuthenticationManager(authenticationManager);
}
I tried several solutions like :
How To Inject AuthenticationManager using Java Configuration in a Custom Filter
Cannot pass AuthenticationManager to custom filter by @Autowired
Spring boot Security Config - authenticationManager must be specified
And I finally picked the first one because I think that it's more useful to have an AuthenticationManager
bean for the project than a CustomFilter
bean used only once.
By the way, what is for you the best solution to implement a filter like this (avoid circular depedencies, better maintenance, ...) ?