Как применить фильтр безопасности spring только на защищенных конечных точках?
У меня есть следующая конфигурация безопасности Spring:
httpSecurity
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/**").fullyAuthenticated()
.and()
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
authenticationTokenFilterBean()
применяется даже в конечных точках, которые не соответствуют выражению /api/**
. Я также попытался добавить следующий код конфигурации
@Override
public void configure(WebSecurity webSecurity) {
webSecurity.ignoring().antMatchers("/some_endpoint");
}
но это еще не решило мою проблему. Как я могу сообщить Spring безопасности применять фильтры только на конечных точках, которые соответствуют безопасному выражению URI? Спасибо вам
Ответы
Ответ 1
У меня есть приложение с тем же требованием, и для его решения я в основном ограничил Spring Безопасность для данного ant совпадения с использованием antMatcher
) следующим образом:
http.antMatcher("/api/**").authorizeRequests() //
.anyRequest().authenticated() //
.and()
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
Вы можете прочитать это следующим образом: для http
используйте эти конфигурации только для запросов, соответствующих шаблону ant /api/**
, разрешающему any request
to authenticated
пользователям and
add filter
authenticationTokenFilterBean()
before
UsernamePasswordAuthenticationFilter
. Для всех остальных эта конфигурация не имеет эффекта.
Ответ 2
Если вы используете
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
Вы можете определить в конструкторе конкретный путь, к которому он будет применяться:
public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
super("/api/**");
this.setAuthenticationManager(authenticationManager);
}
@Override
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
return super.requiresAuthentication(request, response);
}
Метод requireAuthentication будет использоваться для определения необходимости аутентификации этой конечной точки.
Ответ 3
Я думаю, что нашел способ решить это. У меня есть JwtTokenAuthenticationProcessingFilter
, который является AbstractAuthenticationProcessingFilter
. Я хочу, чтобы он аутентифицировал запрос, если в голове есть токен, но не блокировал запрос в случае неудачи. Все, что вам нужно, это переписать doFilter и вызвать chain.doFilter
независимо от того, каков результат проверки подлинности (вызов unsuccessfulAuthentication не является обязательным). Вот часть моего кода.
public class JwtTokenAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
private final TokenExtractor tokenExtractor;
@Autowired
public JwtTokenAuthenticationProcessingFilter(TokenExtractor tokenExtractor, RequestMatcher matcher) {
super(matcher);
this.tokenExtractor = tokenExtractor;
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!this.requiresAuthentication(request, response)) {
chain.doFilter(request, response);
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Request is to process authentication");
}
boolean success = true;
Authentication authResult = null;
try {
authResult = this.attemptAuthentication(request, response);
} catch (InternalAuthenticationServiceException var8) {
this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
success = false;
} catch (AuthenticationException var9) {
success = false;
}
if (success && null != authResult) {
this.successfulAuthentication(request, response, chain, authResult);
}
// Please ensure that chain.doFilter(request, response) is invoked upon successful authentication. You want
// processing of the request to advance to the next filter, because very last one filter
// FilterSecurityInterceptor#doFilter is responsible to actually invoke method in your controller that is
// handling requested API resource.
chain.doFilter(request, response);
}
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
String tokenPayload = request.getHeader(WebSecurityConfig.AUTHENTICATION_HEADER_NAME);
RawAccessJwtToken token = new RawAccessJwtToken(tokenExtractor.extract(tokenPayload));
return getAuthenticationManager().authenticate(new JwtAuthenticationToken(token));
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authResult);
SecurityContextHolder.setContext(context);
}
}
Обновление от 22 апреля.
Чтобы зарегистрировать фильтр, просто добавьте следующий код в WebSecurityConfig
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtAuthenticationProvider mJwtAuthenticationProvider;
@Autowired
public WebSecurityConfig(JwtAuthenticationProvider jwtAuthenticationProvider) {
this.mJwtAuthenticationProvider = jwtAuthenticationProvider;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// When multiple authentication providers are defined, the providers will be queried in the order theyre
// declared.
auth.authenticationProvider(mJwtAuthenticationProvider);
}
}
В коде я раскрыл только критическую часть о добавлении фильтра.
Вся эта реализация была вдохновлена этим сайтом. Отдать должное автору Владимиру Станковичу за его подробное объяснение.
Ответ 4
Чтобы обойти защиту spring для некоторых конкретных конечных точек, выполните следующие действия:
httpSecurity
.authorizeRequests()
.antMatchers("/some_endpoints").permitAll()
.anyRequest().authenticated()
.and()
...