您可以发布多个过滤器链或将您自己的AuthenticationFilter 与AuthenticationManagerResolver 连接
您可以使用AuthenticationManagerResolver 来返回不同的AuthenticationManagers。从 Spring Security 5.4.0 开始,我们不再需要扩展WebSecurityConfigurerAdapter 来配置我们的SecurityFilterChain,而是可以定义一个SecurityFilterChain 类型的bean。
我将详细介绍如何连接您自己的AuthenticationFilter。
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {
http.authorizeRequests((authz) -> authz
.anyRequest().authenticated());
http.addFilterBefore(apiAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
private AuthenticationFilter apiAuthenticationFilter() {
AuthenticationFilter authenticationFilter = new AuthenticationFilter(new ApiAuthenticationManagerResolver(), new BasicAuthenticationConverter());
authenticationFilter.setSuccessHandler((request, response, authentication) -> {});
return authenticationFilter;
}
public static class ApiAuthenticationManagerResolver implements AuthenticationManagerResolver<HttpServletRequest> {
private final Map<RequestMatcher, AuthenticationManager> managers = Map.of(
new AntPathRequestMatcher("/dog/**"), new DogAuthenticationProvider()::authenticate,
new AntPathRequestMatcher("/cat/**"), new CatAuthenticationProvider()::authenticate
);
@Override
public AuthenticationManager resolve(HttpServletRequest request) {
for (Map.Entry<RequestMatcher, AuthenticationManager> entry : managers.entrySet()) {
if (entry.getKey().matches(request)) {
return entry.getValue();
}
}
throw new IllegalArgumentException("Unable to resolve AuthenticationManager");
}
}
public static class DogAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication.getName().endsWith("_dog")) {
return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(),
AuthorityUtils.createAuthorityList("ROLE_DOG"));
}
throw new BadCredentialsException("Username should end with _dog");
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
public static class CatAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication.getName().endsWith("_cat")) {
return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(),
AuthorityUtils.createAuthorityList("ROLE_CAT"));
}
throw new BadCredentialsException("Username should end with _cat");
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
}
在上面的示例中,我们有两个AuthenticationProviders,一个用于猫,另一个用于狗。它们在AntPathRequestMatcher 匹配/dog/** 和/cat/** 端点时解析,在ApiAuthenticationManagerResolver 内。无需为每只狗和猫定义AuthenticationManager,因为AuthenticationProvider/Manager 具有相同的接口。
然后将ApiAuthenticationManagerResolver 连接到过滤器链中的AuthenticationFilter。
您还可以为每个端点定义两个不同的过滤器链,如下所示:
@Bean
public SecurityFilterChain dogApiSecurity(HttpSecurity http) throws Exception {
http.requestMatchers((matchers) -> matchers
.antMatchers("/dog/**"));
http.authorizeRequests((authz) -> authz
.anyRequest().authenticated());
http.httpBasic();
http.authenticationProvider(new DogAuthenticationProvider());
return http.build();
}
@Bean
public SecurityFilterChain catApiSecurity(HttpSecurity http) throws Exception {
http.requestMatchers((matchers) -> matchers
.antMatchers("/cat/**"));
http.authorizeRequests((authz) -> authz
.anyRequest().authenticated());
http.httpBasic();
http.authenticationProvider(new CatAuthenticationProvider());
return http.build();
}
请在定义多个过滤器链时,排序很重要,在这些场景中使用@Order 注释。
当您执行http.requestMatcher(new AntPathRequestMatcher("/endpoint/**")); 时,您是在告诉 Spring Security 仅在请求与该路径匹配时才调用过滤器链。
还有一个ticket within Spring Security's repository 提供了一个接受Map<RequestMatcher, AuthenticationManager> 的AuthenticationManagerResolver 实现,如果您认为它有道理,那就太好了,在那儿给个大拇指。