SpringBoot UsernamePasswordAuthenticationFilter问题

SpringBoot UsernamePasswordAuthenticationFilter issue

我正在扩展 UsernamePasswordAuthenticationFilter 以便我可以添加自定义字段以将它们保存到会话中。

public class AuthFilter extends UsernamePasswordAuthenticationFilter {

@Override
public Authentication attemptAuthentication(HttpServletRequest request,
        HttpServletResponse response) throws AuthenticationException {
    //String dbValue = request.getParameter("dbParam");
    //request.getSession().setAttribute("dbValue", dbValue);
    System.out.println("attempting to authentificate");
    while (request.getAttributeNames().hasMoreElements()) {
        String e = (String) request.getAttributeNames().nextElement();
        System.out.println("param name : " + e + " and param value : " + request.getAttribute(e));
    }

    return super.attemptAuthentication(request, response);
    }
}

还有我的 WebSecurityConfig

@Configuration
@EnableWebMvcSecurity
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private UserDetailsService userDetailsService;


@Bean
public AuthFilter customUsernamePasswordAuthenticationFilter()
        throws Exception {
    AuthFilter customUsernamePasswordAuthenticationFilter = new AuthFilter();
    customUsernamePasswordAuthenticationFilter
            .setAuthenticationManager(authenticationManagerBean());
   return customUsernamePasswordAuthenticationFilter;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.addFilterAfter(customUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

    http.exceptionHandling().accessDeniedPage("/403").and()
            .authorizeRequests().antMatchers("/login", "/public/**").permitAll()
            .antMatchers("/users/**").hasAuthority("ADMIN")
            .anyRequest()
            .authenticated().and().formLogin().loginPage("/login")
            .defaultSuccessUrl("/index").permitAll().and().logout()
            .permitAll();


    http.sessionManagement().maximumSessions(1)
            .expiredUrl("/login?expired").and()
            .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
            .invalidSessionUrl("/");
}

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws  Exception {
    auth.eraseCredentials(false)
    .userDetailsService(userDetailsService);
}

映射过滤器:'customUsernamePasswordAuthenticationFilter' 到:[/*]

所以我确定过滤器已正确添加,但我永远无法打印出里面的内容,因此在验证期间不会调用它。

我使用 Thymeleaf,没有 xml 配置。

作为@M。 Deinum 建议, 我将 UsernamePasswordAuthenticationFilter 更改为 AbstractAuthenticationProcessingFilter,名为 super(new AntPathRequestMatcher("/login","POST")); addFilterAfter 更改为 addFilterBefore 和一些代码,它起作用了!

假设您正在使用最新的 Spring Boot (1.2.3),您正在使用 Spring Security 3.2.7 此版本将 UsernamePasswordAuthenticationFilter 映射到 /j_spring_security_check。但是,当使用基于 java 的配置时,这将更改为 /login

你的仍然映射到旧的 URL。要修复此扩展 AbstractAuthenticationProcessingFilter 添加一个默认的无参数构造函数,它调用采用 RequestMatcher 的超级构造函数。这样做的缺点是,如果您仍然需要(或想要扩展)UsernamePasswordAuthenticationFilter 的功能,您将不得不复制它。

public AuthFilter() {
    super(new AntPathRequestMatcher("/login","POST"));
}

另一个解决方案是仍然扩展 UsernamePasswordAuthenticationFilter 并从那里调用 setRequiresAuthenticationRequestMatcher

public AuthFilter() {
    super();
    setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
}

或者您从工厂方法中调用该方法。

@Bean
public AuthFilter customUsernamePasswordAuthenticationFilter()
    throws Exception {
    AuthFilter customUsernamePasswordAuthenticationFilter = new AuthFilter();
    customUsernamePasswordAuthenticationFilter
        .setAuthenticationManager(authenticationManagerBean());
    customUsernamePasswordAuthenticationFilter
        .setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
    return customUsernamePasswordAuthenticationFilter;
}

您的配置还有另一个问题,您的过滤器将永远不会执行,因为它是在默认 UsernamePasswordAuthenticationFilter 之后执行的,并且身份验证已经发生,您的过滤器将永远不会执行。确保它在默认过滤器之前执行。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.addFilterBefore(customUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    ...
}

为了使您的自定义 UsernamePasswordAuthenticationFilter 实施工作,请将 .loginProcessingUrl("/dologin") 添加到您的 HttpSecurity WebSecurityConfig,这里"/dologin"是html表单元素的action属性值:

@Override
//@Order(Ordered.HIGHEST_PRECEDENCE)
public void configure(HttpSecurity http) throws Exception { // @formatter:off
    http
        ...
        ...
        .formLogin().loginPage("/login")
   -->  .loginProcessingUrl("/dologin") <-- add here
        ...
   -->  .addFilterBefore(new AuthFilter(authenticationManagerBean()),UsernamePasswordAuthenticationFilter.class)
 }

接下来是提供自定义UsernamePasswordAuthenticationFilter实现:

public class AuthFilter extends UsernamePasswordAuthenticationFilter {

   AuthenticationManager authenticationManager;

   private boolean continueChainBeforeSuccessfulAuthentication = false;


   public AuthFilter( AuthenticationManager authenticationManager){
      this.authenticationManager = authenticationManager;
      //idk why I have to do this, otherwise it's null
      super.setAuthenticationManager(authenticationManager);
   }

    public AuthFilter() {}

   private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
   //path to which this filter will intercept
   RequestMatcher customFilterUrl = new AntPathRequestMatcher("/dologin"); <--

  //dofilter method is copied from AbstractAuthenticationProcessingFilter
  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
      HttpServletRequest request = (HttpServletRequest)req;
      HttpServletResponse response = (HttpServletResponse)res;
      //if no match then go to next filter
      if (!customFilterUrl.matches(request)) {
         chain.doFilter(request, response);
      } else {

        Authentication authResult;
        try {
            authResult = this.attemptAuthentication(request, response);
            if (authResult == null) {
                return;
            }

            this.sessionStrategy.onAuthentication(authResult, request, response);
        } catch (InternalAuthenticationServiceException var8) {
            this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
            this.unsuccessfulAuthentication(request, response, var8);
            return;
        } catch (AuthenticationException var9) {
            this.unsuccessfulAuthentication(request, response, var9);
            return;
        }

        if (this.continueChainBeforeSuccessfulAuthentication) {
            chain.doFilter(request, response);
        }

        successfulAuthentication(request, response, chain, authResult);
    }
}

@Override
public Authentication attemptAuthentication(HttpServletRequest request, 
 HttpServletResponse response)
        throws AuthenticationException {

    System.out.println("Your prints"); <--

    return super.attemptAuthentication(request,response);
  }
}