如何在Spring Security中以编程方式配置自定义筛选器?

时间:2014-05-22 15:15:27

标签: java spring spring-security

在用户身份验证上,我需要检索他的远程地址和远程主机 我正在尝试实现自定义过滤器以支持此功能,但我得到“ authenticationManager必须指定”。
另一个疑问是......使用编程方式注册自定义过滤器的正确方法是什么?

使用注释进行配置:

@Configuration
@EnableWebSecurity
public class SecurityApplicationConfiguration extends WebSecurityConfigurerAdapter {

  @Autowired
  private SCAAuthenticationFilter scaAuthenticationFilter;

  @Autowired
  private SCAAuthenticationProvider scaAuthenticationProvider;

  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(scaAuthenticationProvider);
  }

  @Override
  public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/resources/**");
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .addFilter(scaAuthenticationFilter) // What is the right way ?
      .addFilterBefore(scaAuthenticationFilter, AbstractAuthenticationProcessingFilter.class) // What is the right way ?
      .csrf().disable()
      .authorizeRequests()
        .antMatchers("/manual/**").authenticated()
        .and()
      .formLogin()
        .loginPage("/login")
        .loginProcessingUrl("/login")
        .failureUrl("/login?error=true")
        .defaultSuccessUrl("/manual")
        .permitAll()
        .and()
      .logout()
        .logoutUrl("/logout")
        .logoutSuccessUrl("/login")
        .permitAll()
        .and();
  }
}

自定义过滤器:

@Component
public class SCAAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    if (!request.getMethod().equals("POST")) {
      throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
    }
    String username = obtainUsername(request);
    String password = obtainPassword(request);
    String remoteHost = request.getRemoteHost();
    String remoteAddr = request.getRemoteAddr();
    if (username == null) {
      username = "";
    }
    if (password == null) {
      password = "";
    }
    username = username.trim();
    SCAAuthenticationToken scaAuthenticationToken = new SCAAuthenticationToken(username, password, remoteHost, remoteAddr);
    setDetails(request, scaAuthenticationToken);
    return getAuthenticationManager().authenticate(scaAuthenticationToken);
  }

}

4 个答案:

答案 0 :(得分:4)

您需要为扩展过滤器设置authenticationManagerBean并将其配置为corr

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public ExUsernamePasswordAuthenticationFilter exUsernamePasswordAuthenticationFilter()
            throws Exception {
        ExUsernamePasswordAuthenticationFilter exUsernamePasswordAuthenticationFilter = new ExUsernamePasswordAuthenticationFilter();
        exUsernamePasswordAuthenticationFilter
                .setAuthenticationManager(authenticationManagerBean());
        return exUsernamePasswordAuthenticationFilter;
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        RequestMatcher requestMatcher = new RequestMatcher() {
            @Override
            public boolean matches(HttpServletRequest httpServletRequest) {
                if (httpServletRequest.getRequestURI().indexOf("/api", 0) >= 0) {
                    return true;
                }
                return false;
            }
        };

        http
                .addFilterBefore(exUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                ...
    }
}

答案 1 :(得分:2)

您的自定义过滤器扩展了Spring Security的UsernamePasswordAuthenticationFilter,这意味着它需要对身份验证管理器的引用。我会在安全配置中将您的过滤器设置为@Bean,然后按照this answer说明获取AuthenticationManager引用的不同选项。

答案 2 :(得分:1)

在扩展WebSecurityConfigurerAdapter的类中,重写authenticationManagerBean()方法并使用@Bean注释它:

@Configuration
@EnableWebMvcSecurity
public class YourCustomConfig extends WebSecurityConfigurerAdapter{

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

现在,您将能够在其他类中@Autowire AuthenticationManager。

答案 3 :(得分:0)

另一个选择是为过滤器创建一个配置器,并将所有与过滤器初始化有关的工作委托给它(通过UsernamePasswordAuthenticationFilter配置FormLoginConfigurerAbstractAuthenticationProcessingFilter的配置方式相同) AbstractAuthenticationFilterConfigurer)。

public class SCAAuthenticationConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>() {

    public static SCAAuthenticationConfigurer scaAuthentication() {
        return new SCAAuthenticationConfigurer()
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {

        SCAAuthenticationFilter filter = new SCAAuthenticationFilter()
        filter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));

        // add postProcess(filter) call if require to autowire some fields

        http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class)
    }
}

有了这样的配置器,您的SecurityConfig会看起来更整洁:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .apply(scaAuthentication())
            .and()

            // do the rest of configuration
    }
}

您甚至可以将过滤器初始化委派给ApplicationContext(例如,如果您有要注入的配置):

public class FilterWithSettingsConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>() {

    @Configuration
    @EnableConfigurationProperties(SomeSettings.class)
    private static class Config {}

    @Override
    public void configure(HttpSecurity http) throws Exception {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()
        context.parent = http.getSharedObject(ApplicationContext.class)
        context.register(Config.class)
        context.refresh()

        FilterWithSettings filter = 
            context.getAutowireCapableBeanFactory().createBean(FilterWithSettings.class)

        filter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));

        http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class)
    }
}

有关综合示例,请查看https://github.com/shiver-me-timbers/smt-spring-security-parent/blob/master/smt-spring-security-jwt/src/main/java/shiver/me/timbers/spring/security/JwtSpringSecurityAdaptor.java