Home > Software design >  Spring Boot - Custom Authentication Filter without using the WebSecurityConfigurerAdapter
Spring Boot - Custom Authentication Filter without using the WebSecurityConfigurerAdapter

Time:08-17

I am trying to migrate a code that used the old springboot security configuration with the WebSecurityConfigurerAdapter to the new component based security config, i have a custom authentication filter that extends UsernamePasswordAuthenticationFilter that want to include in my SecurityFilterChain bean as is showed here:

SecurityConfig.java file

package com.tito.userservice.security;

import com.tito.userservice.filter.CustomAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;


@Configuration @EnableWebSecurity @RequiredArgsConstructor
public class SecurityConfiguration {

    private final UserDetailsService userDetailsService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    @Bean
    public PasswordEncoder encoder() {
        return new BCryptPasswordEncoder();
    }


    //----------------HTTP SECURITY CONFIG-------------------------------------
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http
                .authorizeRequests().anyRequest().permitAll();
               

        http.addFilter(new CustomAuthenticationFilter(authenticationManager()));

        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)
            throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    //----------------HTTP SECURITY CONFIG-------------------------------------



}

for that I created a Bean of type AuthenticationManager and tryed to use it in my CustomAuthenticationFilter but i got the following error: error in the CustomAuthenticationFilter when passing authenticationManager as arg

the code of my CustomAuthenticationFilter is :

CustomAuthenticationFilter.java file

package com.tito.userservice.filter;


import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.tito.userservice.domain.AppUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.core.userdetails.User;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.Date;
import java.util.stream.Collectors;

@Slf4j
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;

    public CustomAuthenticationFilter(AuthenticationManager authenticationManager){
        this.authenticationManager = authenticationManager;
    }
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        log.info("Username is: {}", username); log.info("Password is: {}",password);
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,password);
        return  authenticationManager.authenticate(authenticationToken);
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
        User user = (User)authentication.getPrincipal();
        Algorithm algorithm = Algorithm.HMAC256("secret".getBytes()); 
        String access_token = JWT.create()
                .withSubject(user.getUsername())
                .withExpiresAt(new Date(System.currentTimeMillis()  10*60*1000))
                .withIssuer(request.getRequestURL().toString())
                .withClaim("roles",user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
                .sign(algorithm);
        String refresh_token = JWT.create()
                .withSubject(user.getUsername())
                .withExpiresAt(new Date(System.currentTimeMillis()  30*60*1000))
                .withIssuer(request.getRequestURL().toString())
                .sign(algorithm);
        response.setHeader("acces_token",access_token);
        response.setHeader("refres_token",refresh_token);
    }
}

there you can see that the constructor expects a AuthenticationManager argument so thats why i pass the created bean as the argument, how can i fix this, is there something i am missing? also the stack trace when running the app is:

[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /C:/Users/user/workspace/spring-security/userservice/src/main/java/com/tito/userservice/security/SecurityConfiguration.java:[47,55] method authenticationManager in class com.tito.userservice.security.SecurityConfiguration cannot be applied to given types;
  required: org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
  found:    no arguments
  reason: actual and formal argument lists differ in length
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  4.968 s
[INFO] Finished at: 2022-08-16T13:01:40-05:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.10.1:compile (default-compile) on project userservice: Compilation failure
[ERROR] /C:/Users/user/workspace/spring-security/userservice/src/main/java/com/tito/userservice/security/SecurityConfiguration.java:[47,55] method authenticationManager in class com.tito.userservice.security.SecurityConfiguration cannot be applied to given types;
[ERROR]   required: org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
[ERROR]   found:    no arguments
[ERROR]   reason: actual and formal argument lists differ in length
[ERROR]
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

CodePudding user response:

You can inject a bean of type AuthenticationManagerBuilder into your SecurityConfiguration and utilize its getOrBuild() method, which doesn't raise exceptions, to get an instance of AuthenticationManager.

So, your SecurityConfiguration might look like this:

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {

    private final UserDetailsService userDetailsService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;
    private final AuthenticationManagerBuilder authManagerBuilder;

    // some beans

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http.csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests().anyRequest().permitAll()
            .and()
            .addFilter(new CustomAuthenticationFilter(authManagerBuilder.getOrBuild()));

        return http.build();
    }
}
  • Related