Как применить фильтр безопасности 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()
     ...