Java 如何仅在安全端点上应用 spring 安全过滤器?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36795894/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to apply spring security filter only on secured endpoints?
提问by Bravo
I have the following Spring Security configuration:
我有以下 Spring Security 配置:
httpSecurity
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/**").fullyAuthenticated()
.and()
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
The authenticationTokenFilterBean()
is applied even on endpoints that do not match /api/**
expression. I also tried adding the following configuration code
该authenticationTokenFilterBean()
甚至在不匹配的终端应用/api/**
表现。我也尝试添加以下配置代码
@Override
public void configure(WebSecurity webSecurity) {
webSecurity.ignoring().antMatchers("/some_endpoint");
}
but this still did not solve my problem. How can I tell spring security to apply filters only on endpoints that match the secured URI expression? Thank you
但这仍然没有解决我的问题。如何告诉 spring security 仅在与安全 URI 表达式匹配的端点上应用过滤器?谢谢
采纳答案by Francisco Spaeth
I have an application with the same requirement and to solve it I basically restricted Spring Security to a given ant match patter (using antMatcher
) as follows:
我有一个具有相同要求的应用程序,为了解决它,我基本上将 Spring Security 限制为给定的 ant 匹配模式(使用antMatcher
),如下所示:
http.antMatcher("/api/**").authorizeRequests() //
.anyRequest().authenticated() //
.and()
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
You can read it as follows: for http
only invoke these configurations on requests matching the ant pattern /api/**
authorizing any request
to authenticated
users and
add filter
authenticationTokenFilterBean()
before
UsernamePasswordAuthenticationFilter
. For all others requests this configuration has no effect.
你可以这样理解:for http
only invoke这些配置对匹配ant模式/api/**
授权any request
给authenticated
用户的请求and
add filter
authenticationTokenFilterBean()
before
UsernamePasswordAuthenticationFilter
。对于所有其他请求,此配置无效。
回答by phoenix
To bypass spring security for some specific endpoints do the following:
要绕过某些特定端点的 spring 安全性,请执行以下操作:
httpSecurity
.authorizeRequests()
.antMatchers("/some_endpoints").permitAll()
.anyRequest().authenticated()
.and()
...
回答by Qualaelay
If you use the
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
如果您使用
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
You can define in the constructor the specific path it will apply to:
您可以在构造函数中定义它将应用于的特定路径:
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);
}
The requiresAuthentication method will be used to know if that endpoint needs authentication
requiresAuthentication 方法将用于知道该端点是否需要身份验证
回答by Shengfeng Li
I think I've found a way to solve it. I have JwtTokenAuthenticationProcessingFilter
which is an AbstractAuthenticationProcessingFilter
. I want it to authenticate request if there is token in the head but do not block the request if failed. All you need is to rewrite the doFilterand invoke the chain.doFilter
no matter what the authentication result is(invoking unsuccessfulAuthentication is optional). Here is part of my code.
我想我已经找到了解决它的方法。我有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);
}
}
Update at Apr 22
4月22日更新
To register the filter, just add the following code to the WebSecurityConfig
要注册过滤器,只需将以下代码添加到 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);
}
}
In the code, I only revealed the critical part about adding the filter. All this implementation was inspired by this site. Give credit to the author Vladimir Stankovic for his detail explanation.
在代码中,我只透露了添加过滤器的关键部分。所有这些实现都受到了这个站点的启发。感谢作者 Vladimir Stankovic 的详细解释。
回答by Sa?a ?ijak
GenericFilterBean
has a following method :
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;
}
So in your filter that extends GenericFilterBean
you can override that method and implement logic to run the filter only on the routes that you would like.
因此,在扩展的过滤器中,GenericFilterBean
您可以覆盖该方法并实现逻辑以仅在您想要的路由上运行过滤器。