首页 > 解决方案 > AuthenticationManager 在我的自定义过滤器中为空

问题描述

我的自定义过滤器基于UsernamePasswordAuthenticationFilter需要,AuthenticationManager但每次我调用该方法attemptAuthentication()时,编译都会在此处失败:

Authentication auth2 = this.getAuthenticationManager().authenticate(authRequest);

AuthenticationManager空:

java.lang.NullPointerException: null
    at app.shellx.security.CustomUsernamePasswordAuthenticationFilter.attemptAuthentication(CustomUsernamePasswordAuthenticationFilter.java:75) ~[classes/:na]

网络安全配置

@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);
            }
        };
    }
}

自定义过滤器

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);
    }

我尝试了几种解决方案,例如:

如何在自定义过滤器中使用 Java 配置注入 AuthenticationManager

无法通过 @Autowired 将 AuthenticationManager 传递给自定义过滤器

Spring boot Security Config - 必须指定 authenticationManager

我最终选择了第一个,因为我认为AuthenticationManager为项目提供一个CustomFilterbean 比只使用一次的 bean 更有用。

顺便说一句,对您来说,实现这样的过滤器的最佳解决方案是什么(避免循环依赖,更好的维护......)?

标签: javaspringsecurityauthenticationspring-security

解决方案


CustomUsernamePasswordAuthenticationFilter不是由 Spring 管理的(因为您直接创建了它),因此您的过滤器中没有 Spring 管理的依赖注入行为。这就是为什么AuthenticationManager从未注入并且现在为空的原因。

假设你把你的暴露AuthenticationManager为一个豆子......

  1. 您可以Filter通过@Bean您的WebSecurityConfig

  2. 或者,您可以在AuthenticationManager创建过滤器对象时简单地将 传递给您的过滤器(通过其构造函数或设置器)。无需将过滤器公开为 bean。


推荐阅读