Java 如何仅在安全端点上应用spring安全筛选器?

Java 如何仅在安全端点上应用spring安全筛选器?,java,spring,security,spring-mvc,spring-security,Java,Spring,Security,Spring Mvc,Spring Security,我有以下Spring安全配置: httpSecurity .csrf() .disable() .exceptionHandling() .authenticationEntryPoint(unauthorizedHandler) .and() .sessionManagement() .sessionCreationPo

我有以下Spring安全配置:

    httpSecurity
            .csrf()
            .disable()
            .exceptionHandling()
            .authenticationEntryPoint(unauthorizedHandler)
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
            .antMatchers("/api/**").fullyAuthenticated()
            .and()
            .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
即使在与
/api/**
表达式不匹配的端点上,也会应用
authenticationTokenFilterBean()
。我还尝试添加以下配置代码

@Override
public void configure(WebSecurity webSecurity) {
    webSecurity.ignoring().antMatchers("/some_endpoint");
}

但这仍然没有解决我的问题。我如何告诉spring安全性仅在匹配安全URI表达式的端点上应用过滤器?感谢您

要绕过某些特定端点的spring安全性,请执行以下操作:

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

我有一个具有相同需求的应用程序,为了解决它,我基本上将Spring Security限制为给定的ant匹配模式(使用
antMatcher
),如下所示:

http.antMatcher("/api/**").authorizeRequests() //
        .anyRequest().authenticated() //
        .and()
        .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
/**
 * 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();
}
您可以阅读如下内容:对于
http
,仅在与ant模式匹配的请求上调用这些配置
/api/**
授权
任何请求
已验证的
用户
添加过滤器
验证令牌过滤器bean()
之前
用户名密码身份验证过滤器
。对于所有其他请求,此配置无效。

如果使用

.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
方法将用于了解端点是否需要身份验证。

我想我已经找到了解决方法。我有一个
JwtTokenAuthenticationProcessingFilter
,它是一个
AbstractAuthenticationProcessingFilter
。如果头部有令牌,我希望它对请求进行身份验证,但如果失败,不要阻止请求。您只需重写doFilter并调用
链。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);
    }
}
在代码中,我只揭示了添加过滤器的关键部分。
所有这些实现都是受其启发的。感谢作者弗拉基米尔·斯坦科维奇的详细解释。

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;
    }
/**
*可以在自定义筛选控件的子类中重写,
*返回{@code true}以避免过滤给定的请求。
*默认实现总是返回{@code false}。
*@param请求当前HTTP请求
*@return是否不应筛选给定的请求
*@在出现错误时抛出ServletException
*/
受保护的布尔值shouldNotFilter(HttpServletRequest请求)引发ServletException{
返回false;
}

因此,在扩展了
GenericFilterBean
的筛选器中,您可以重写该方法并实现逻辑,以便仅在您想要的路由上运行筛选器。

我的要求是排除端点匹配/api/auth/**,以实现相同的效果,我已将我的WebSecurityConfig spring配置组件配置为:

http.antMatcher("/api/**").authorizeRequests() //
        .anyRequest().authenticated() //
        .and()
        .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
/**
 * 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();
}


有没有一种方法可以指定适用于特定筛选器而不是终结点的路径?您是指类似于/some_endpoint/**的路径,其中包括/some_endpoint/path1等?。。。那么是的。。。让antMatcher接受
/api/**
谢谢你的回答,@phoenix。不幸的是,这并不能解决我的问题。筛选器仍适用于“/some_endpoints”URL仍在应用哪个筛选器?如果您想要两个不同的httpSecurity元素作为解决方案。。。那对你来说非常合适。。。我可以向您推荐一些解决方案,如果我想完全允许/api/login,请参阅旁路/api/login。即使我执行permitAll(),过滤器仍然会被调用。请建议。那不是真的。。。
authenticationTokenFilterBean
将在它仅与anonymous一起工作的每个请求上运行。它对我不起作用,对请求调用筛选器,而不是/api对我也不起作用,对上的请求执行筛选器。例如/internal对注释的进一步读者:答案正确。所有说它不起作用的人都会做错事(例如,他们定义了
authenticationTokenFilterBean()
方法为
@Bean
,在这种情况下,spring boot将自动扫描它并将其添加为通用筛选器,即使没有此安全配置,如果您只想将此筛选器添加到安全筛选器链中,这显然是错误的)。奇怪,我得到了完全相反的问题——我的授权筛选器只在显式要求身份验证的端点上被命中,我希望它们在所有情况下都被触发。如何在WebSecurityConfigureAdapter类中注册此筛选器???@mosean Hi那里。我已经更新了答案,以显示如何在WebSecurity ConfigureAdapter中注册筛选器。@ShengfengLi我可以使用同一个筛选器执行captcha验证,还是需要另一个超类或interface@NeelamKapoor你好。您可以根据需要使用过滤器,也可以使用新过滤器,然后将其注册到适配器。这取决于你如何实现代码。我在javadoc中找不到这个。你确定这个存在吗?编辑:我发现它被移动到了
OncePerRequestFilter
,但是谢谢你指向了正确的方向。非常感谢,这解决了我的问题!我不能使用其他地方提到的
/api/**
方法,因此这适合我的用例。你能解释一下为什么会这样吗?
WebSecurity
是否在链中首先被调用?我只是想知道为什么它对
有效。忽略
WebSecurity
上的
HttpSecurity
荣誉的端点。