Eug*_*yer 3 rest spring-mvc spring-security
由于 StackOverflow 上已经有很多关于此问题的问题,我首先想确保这不是重复和区分的。
这是关于
所以问题不在于:
假设有 2 个自定义 AuthenticationProvider:CATApiTokenProvider和DOGApiTokenProvider。我们故意不谈论 AOuth/JWT/Basic/Form 提供者,因为它们提供了快捷方式。
现在我们有 2 个 REST API 端点/dog/endpoint和/cat/endpoint.
今天,如何使用 Spring Security 5.5 正确实现这一点:
CATApiTokenProvider只能对以下请求进行身份验证/cat/endpointDOGApiTokenProvider只能对以下请求进行身份验证/dog/endpoint因此,无法使用 cat 令牌进行身份验证/dog/endpoint,也无法使用狗令牌进行身份验证/cat/endpoint。
我的想法/方法
a) 据我所知,由于我有自定义的 Cat/Dog 过滤器,因此可以在创建 bean 时使用AuthenticationManagerResolver并将一个实例传递到过滤器中。这个解析器可能看起来像
public AuthenticationManagerResolver<HttpServletRequest> resolver()
{
return request -> {
if (request.getPathInfo().startsWith("/dog/")) {
try {
return ???;
} catch (Exception exception) {
log.error(exception);
}
}
if (request.getPathInfo().startsWith("/cat/")) {
try {
return ???;
} catch (Exception exception) {
log.error(exception);
}
}
};
}
Run Code Online (Sandbox Code Playgroud)
两个问题是:
public void configure(AuthenticationManagerBuilder auth),但据我所知,我只会配置“一个”AuthenticationManager,我可以在那里添加 DogAP 和 CatAP,但这会让 1 AM 具有 2 个 AP,所以当使用这个 AM 时,我可以使用cat 端点上的狗令牌b) 以某种方式实例化 2 个不同的 AuthenticationManager,然后使用 SecurityConfiguration 将它们分配给不同的匹配器。
两个问题:
http.authorizeRequests()
.antMatchers("/dog/**")
.?
Run Code Online (Sandbox Code Playgroud)
您可以发布多个过滤器链或将您自己的AuthenticationFilter过滤器链与AuthenticationManagerResolver
您可以使用AuthenticationManagerResolver返回不同的AuthenticationManagers。从 Spring Security 5.4.0 开始,我们不再需要扩展WebSecurityConfigurerAdapter来配置我们的SecurityFilterChain,您可以定义一个 beanSecurityFilterChain类型。
我将详细介绍您自己的接线AuthenticationFilter。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,我们有两个AuthenticationProviders,一个代表 cat,另一个代表狗。它们是根据 . 内部和端点的AntPathRequestMatcher匹配来解析的。无需为每只狗和猫定义一个,因为它们具有相同的接口。/dog/**/cat/**ApiAuthenticationManagerResolverAuthenticationManagerAuthenticationProvider/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();
}
Run Code Online (Sandbox Code Playgroud)
@Order请在定义多个过滤器链时,顺序很重要,请在这些场景中使用注释。
当您这样做时,http.requestMatcher(new AntPathRequestMatcher("/endpoint/**"));您是在告诉 Spring Security 仅在请求与该路径匹配时调用过滤器链。
Spring Security 的存储库中还有一个票证,提供了一个AuthenticationManagerResolver接受的实现Map<RequestMatcher, AuthenticationManager>,如果您认为它有道理,那就太好了,请在那里竖起大拇指。
| 归档时间: |
|
| 查看次数: |
2289 次 |
| 最近记录: |