Я пытаюсь создать фильтр с пружинной защитой 5 и загрузкой 2, который защищает некоторые, но не все конечные точки с ключом API и без сеансов. Однако после того, как фильтр аутентифицируется, он устает перенаправлять на «/» вместо исходного URL-адреса, поскольку SavedRequestAwareAuthenticationSuccessHandler имеет кэш запроса empy и возвращается к значению по умолчанию "/".
Как я могу заставить его продолжить целевой ресурс вместо '/' и почему он работает так?
Я также был бы признателен, если бы кто-нибудь мог объяснить мне, почему AbstractPreAuthenticatedProcessingFilter предназначен для продолжения цепочки фильтров после успешной аутентификации, но AbstractAuthenticationProcessingFilter не.
Это класс конфигурации безопасности:
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${secret.admin.api.key}")
String validApiKey;
@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.authenticationProvider(new ApiKeyAuthenticationProvider(validApiKey));
}
@Override
protected void configure(HttpSecurity security) throws Exception {
security
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(STATELESS)
.and()
.addFilterBefore(new ApiKeyAuthenticationFilter(authenticationManager()), AnonymousAuthenticationFilter.class)
.requestMatchers()
.antMatchers("/v1/admin/**")
.antMatchers("/actuator/**")
.and()
.authorizeRequests().anyRequest().authenticated();
}
}
Мой фильтр сопоставляет запросы с заголовком авторизации с пользовательским значением. Извлекает ключ api из заголовка и передает маркер с принципалом и учетными данными (ключ api) менеджеру аутентификации.
public class ApiKeyAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
//...
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
var apiKey = extractApiKey(request.getHeader(AUTHORIZATION));
var token = new ApiKeyAuthenticationToken(apiKey);
var authentication = getAuthenticationManager().authenticate(token);
return authentication;
}
//...
}
Следующий класс проверяет учетные данные и создает и аутентифицирует токен с принципалом и без учетные данные:
public class ApiKeyAuthenticationProvider implements AuthenticationProvider {
// ...
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
var apiKey = (String) authentication.getCredentials();
if (validApiKey.equals(apiKey)) {
var auth = new ApiKeyAuthenticationToken();
auth.setAuthenticated(true);
return auth;
} else {
throw new BadCredentialsException("Bad ApiKey credentials");
}
}
@Override
public boolean supports(Class<?> authentication) {
return ApiKeyAuthenticationToken.class.isAssignableFrom(authentication);
}
}
И журналы:
OrRequestMatcher: Trying to match using Ant [pattern='/v1/admin/**']
AntPathRequestMatcher: Checking match of request : '/v1/admin/namespace'; against '/v1/admin/**'
OrRequestMatcher: matched
FilterChainProxy$VirtualFilterChain: /v1/admin/namespace at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
FilterChainProxy$VirtualFilterChain: /v1/admin/namespace at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
FilterChainProxy$VirtualFilterChain: /v1/admin/namespace at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
FilterChainProxy$VirtualFilterChain: /v1/admin/namespace at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
[...]
FilterChainProxy$VirtualFilterChain: /v1/admin/namespace at position 5 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
FilterChainProxy$VirtualFilterChain: /v1/admin/namespace at position 6 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
FilterChainProxy$VirtualFilterChain: /v1/admin/namespace at position 7 of 11 in additional filter chain; firing Filter: 'ApiKeyAuthenticationFilter'
ProviderManager: Authentication attempt using ....security.ApiKeyAuthenticationProvider
AbstractAuthenticationTargetUrlRequestHandler: Using default Url: /
DefaultRedirectStrategy: Redirecting to '/'
Я пытался сделать эту работу на основе this .