在 Spring Boot 2.0.3 应用程序中,我想根据请求路径应用多种身份验证方法。
预期行为
对于请求匹配;
- /静止的/**
- 不要应用任何身份验证过滤器,允许所有
- /安全的/**
- 期望并验证客户端证书
- /api/**
- 通过 HTTP Basic 或 HTTP Digest 进行身份验证
观察到的行为
无论我请求哪个 URL,所有身份验证过滤器始终按以下顺序应用;
- X509AuthenticationFilter
- 基本身份验证过滤器
- CustomDigestAuthenticationFilter
过滤器按预期工作,我使用任何方法进行身份验证都没有问题。但在某些边缘情况下,应用整个 FilterChain 会导致问题,例如,如果 Web 浏览器缓存了 Authorization 标头,则 Web 浏览器不会要求客户端证书。
所以主要问题是;如何将不同的过滤器隔离到其特定的请求域?
更新/编辑
从Spring Security 的多个 url 规则集不一起工作 和Spring Security Reference跟随输入。不幸的是,这种行为也适用于这种方法。
WebSecurityConfig 类
@EnableWebSecurity
public class WebSecurityConfig {
private final CertificateAuthenticationProvider certificateAuthenticationProvider;
private final CustomBasicAuthenticationProvider customBasicAuthenticationProvider;
@Autowired
public WebSecurityConfig(CertificateAuthenticationProvider certificateAuthenticationProvider, CustomBasicAuthenticationProvider customBasicAuthenticationProvider) {
this.certificateAuthenticationProvider = certificateAuthenticationProvider;
this.customBasicAuthenticationProvider = customBasicAuthenticationProvider;
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(certificateAuthenticationProvider);
auth.authenticationProvider(customBasicAuthenticationProvider);
}
@Configuration
@Order(1)
public static class SecureTestbedWebConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http .antMatcher("/secure/**").authorizeRequests().anyRequest().authenticated()
.and().x509().x509AuthenticationFilter(x509AuthenticationFilter())
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
}
@Bean
public CustomX509AuthenticationFilter x509AuthenticationFilter() {
CustomX509AuthenticationFilter customX509AuthenticationFilter = new CustomX509AuthenticationFilter();
customX509AuthenticationFilter.setAuthenticationManager(authenticationManager());
return customX509AuthenticationFilter;
}
@Bean
@Override
protected AuthenticationManager authenticationManager() {
try {
return super.authenticationManager();
} catch (Exception ex) {
throw new IllegalStateException("Failed to extract AuthenticationManager.", ex);
}
}
}
@Configuration
@Order(2)
public static class ApiAuthenticationConfigurationAdapter extends WebSecurityConfigurerAdapter {
private final CustomBasicAuthenticationEntryPoint customBasicAuthenticationEntryPoint;
private final AccessDao accessDao;
@Autowired
public ApiAuthenticationConfigurationAdapter(CustomBasicAuthenticationEntryPoint customBasicAuthenticationEntryPoint, AccessDao accessDao) {
this.customBasicAuthenticationEntryPoint = customBasicAuthenticationEntryPoint;
this.accessDao = accessDao;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http .antMatcher("/api/**")
.requestMatcher(new BasicRequestMatcher())
.authorizeRequests().anyRequest().authenticated()
.and().httpBasic().authenticationEntryPoint(customBasicAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and().addFilterAfter(customDigestAuthenticationFilter(), BasicAuthenticationFilter.class);
}
@Bean
public CustomDigestAuthenticationFilter customDigestAuthenticationFilter() {
return new CustomDigestAuthenticationFilter(accessDao);
}
}
@Configuration
@Order(3)
public static class NoAuthenticationNeededConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http .antMatcher("/static/**").authorizeRequests()
.antMatchers(HttpMethod.GET).permitAll();
}
}
private static class BasicRequestMatcher implements RequestMatcher {
@Override
public boolean matches(HttpServletRequest request) {
String auth = request.getHeader("Authorization");
boolean isMatching = auth != null && (auth.startsWith("Basic") || auth.startsWith("Digest"));
log.debug("HTTP {} {}, isMatching; {}", request.getMethod(), request.getRequestURI(), isMatching);
return isMatching;
}
}
}