簡體   English   中英

如何僅在安全端點上應用 Spring 安全過濾器?

[英]How to apply Spring Security filter only on secured endpoints?

我有以下 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 Security 僅在與安全 URI 表達式匹配的端點上應用過濾器?

我有一個具有相同要求的應用程序,為了解決它,我基本上將 Spring Security 限制為給定的 ant 匹配模式(使用antMatcher ),如下所示:

http.antMatcher("/api/**").authorizeRequests() //
        .anyRequest().authenticated() //
        .and()
        .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);

您可以按如下方式閱讀:對於http僅在匹配螞蟻模式的請求上調用這些配置/api/**授權any request到經過authenticated用戶, and before UsernamePasswordAuthenticationFilter before add filter authenticationTokenFilterBean() 對於所有其他請求,此配置無效。

GenericFilterBean有以下方法:

/**
     * Can be overridden in subclasses for custom filtering control,
     * returning {@code true} to avoid filtering of the given request.
     * <p>The default implementation always returns {@code false}.
     * @param request current HTTP request
     * @return whether the given request should <i>not</i> be filtered
     * @throws ServletException in case of errors
     */
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        return false;
    }

因此,在擴展GenericFilterBean的過濾器中,您可以覆蓋該方法並實現邏輯以僅在您想要的路由上運行過濾器。

如果您使用

.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);
    }

requiresAuthentication方法將用於知道該端點是否需要身份驗證。

我的要求是排除匹配 /api/auth/** 的端點,以實現相同的我配置我的 WebSecurityConfig spring 配置組件如下:

/**
 * The purpose of this method is to exclude the URL's specific to Login, Swagger UI and static files.
 * Any URL that should be excluded from the Spring security chain should be added to the ignore list in this
 * method only
 */
@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/api/auth/**","/v2/api-docs", 
            "/configuration/ui", 
            "/swagger-resources", 
            "/configuration/security",
            "/swagger-ui.html", 
            "/webjars/**",
            "/favicon.ico",
            "/**/*.png",
            "/**/*.gif",
            "/**/*.svg",
            "/**/*.jpg",
            "/**/*.html",
            "/**/*.css",
            "/**/*.js");
}


   /**
     * The purpose of this method is to define the HTTP configuration that defines how an HTTP request is 
     * going to be treated by the Spring Security chain. All the request URL's (excluding the URL's added
     * in WebSecurity configuration ignore list) matching this configuration have to pass through the
     * custom Spring security filter defined in this method
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
        .cors().disable()
        .authorizeRequests()
        .anyRequest()
        .authenticated()
        .and()
        .exceptionHandling()
        .authenticationEntryPoint(unauthorizedHandler)
        .and()
        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
    }

/**
 * The purpose of this method is to create a new instance of JWTAuthenticationFilter
 * and return the same from the method body. It must be ensured that this filter should
 * not be configured as a Spring bean or registered into the Spring Application context
 * failing which the below filter shall be registered as a default web filter, and thus
 * all the URL's even the excluded ones shall be intercepted by the below filter
 */
public JWTAuthenticationFilter authenticationTokenFilterBean() {
    return new JWTAuthenticationFilter();
}

我想我已經找到了解決它的方法。 我有JwtTokenAuthenticationProcessingFilter ,它是一個AbstractAuthenticationProcessingFilter 如果頭部有令牌,我希望它對請求進行身份驗證,但如果失敗則不阻止請求。 無論認證結果如何(調用 unsuccessfulAuthentication 是可選的),您只需要重寫doFilter並調用chain.doFilter 這是我的代碼的一部分。

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);
    }
}

4月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 they’re
        // declared.
        auth.authenticationProvider(mJwtAuthenticationProvider);
    }
}

在代碼中,我只透露了有關添加過濾器的關鍵部分。 所有這些實現都受到了這個站點的啟發。 感謝作者 Vladimir Stankovic 的詳細解釋。

我們最近更新到 Spring Boot 3.0.0,它使用 Spring Security 6.0.0,並且在將過濾器應用於所有請求時遇到了類似的問題,盡管authorizeHttpRequests()與定義的特定路徑一起使用。

原來,如果要為特定路徑配置HttpSecurity ,則需要在開始時使用securityMatcher()

所以它會是這樣的:

private SecurityFilterChain configureFilterChain(HttpSecurity http, String pattern, String... roles) throws Exception {
        return http
                .securityMatcher(pattern)
                .authorizeHttpRequests(auth -> auth.requestMatchers(AntPathRequestMatcher.antMatcher(pattern)).hasAnyRole(roles))
                .addFilterBefore(new TokenFilter(), UsernamePasswordAuthenticationFilter.class)
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(new AuthenticationEntryPointImpl())
                .accessDeniedHandler(new AccessDeniedHandlerImpl())
                .and()
                .csrf().disable()
                .build();
    }

因此,在這種情況下,TokenFilter 將僅應用於具有此pattern的請求。

要繞過某些特定端點的 spring 安全性,請執行以下操作:

httpSecurity
     .authorizeRequests()
     .antMatchers("/some_endpoints").permitAll()
     .anyRequest().authenticated()
     .and()
     ...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM