我正在使用 Spring security 和 jwt ,但配置文件中有一些我不理解的东西(JWT 上的所有教程中的配置相同) 这就是为什么要在
UsernamePasswordAuthenticationFilter
之前添加自定义 jwt 过滤器,因为我已经在项目的某个位置有一个基于用户名和密码的公共身份验证控制器,为什么不以其他顺序添加它?
@Bean
public JwtAuthTokenFilter authenticationJwtTokenFilter() {
return new JwtAuthTokenFilter();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint((AuthenticationEntryPoint) unauthorizedHandler)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore((Filter) authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
TL;博士;
我相信这只是互联网上某人碰巧选择并在某处共享他/她的代码的随机过滤器,从那以后就一直采用这种方式。
对于好奇者
我亲自浏览了Spring Security源代码,发现Spring应用程序上下文中的过滤器及其各自的
@Order
如下:
org.springframework.security.web.access.channel.ChannelProcessingFilter -> 100
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter -> 300
org.springframework.security.web.context.SecurityContextPersistenceFilter -> 400
org.springframework.security.web.header.HeaderWriterFilter -> 500
org.springframework.web.filter.CorsFilter -> 600
org.springframework.security.web.csrf.CsrfFilter -> 700
org.springframework.security.web.authentication.logout.LogoutFilter -> 800
org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter -> 900
org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter -> 1000
org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter -> 1100
org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter -> 1200
org.springframework.security.cas.web.CasAuthenticationFilter -> 1300
org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter -> 1400
org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter -> 1500
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter -> 1600
org.springframework.security.openid.OpenIDAuthenticationFilter -> 1800
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter -> 1900
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter -> 2000
org.springframework.security.web.session.ConcurrentSessionFilter -> 2100
org.springframework.security.web.authentication.www.DigestAuthenticationFilter -> 2200
org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter -> 2300
org.springframework.security.web.authentication.www.BasicAuthenticationFilter -> 2400
org.springframework.security.web.savedrequest.RequestCacheAwareFilter -> 2500
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter -> 2600
org.springframework.security.web.jaasapi.JaasApiIntegrationFilter -> 2700
org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter -> 2800
org.springframework.security.web.authentication.AnonymousAuthenticationFilter -> 2900
org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter -> 3000
org.springframework.security.web.session.SessionManagementFilter -> 3100
org.springframework.security.web.access.ExceptionTranslationFilter -> 3200
org.springframework.security.web.access.intercept.FilterSecurityInterceptor -> 3300
org.springframework.security.web.authentication.switchuser.SwitchUserFilter -> 3400
请注意,顺序值越小,过滤器的优先级越高。
但是这些都是Spring注册的过滤器。另一方面,Spring Security 是一个内部包含多个过滤器的单个过滤器链,仅包含其中的一些过滤器,它们是(按优先级顺序):
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CorsFilter
LogoutFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
您可以通过在 Spring Security 配置中将调试标志设置为 true 来获取此列表
@EnableWebSecurity(debug = true)
自定义 Jwt Filter 放在哪里?
我个人会将自定义 Jwt 过滤器放置在
ExceptionTranslationFilter
之后和 FilterSecurityInterceptor
之前,形成以下链:
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CorsFilter
LogoutFilter
<------ Here's where the UsernamePasswordAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
JwtAuthTokenFilter <------ Here's your filter.
FilterSecurityInterceptor
]
您会看到这样做的动机,是让 Spring 安全异常处理程序驻留在
ExceptionTranslatorFilter
中按原样处理您的异常。
因为如果您将过滤器放置在 UsernamePasswordAuthenticationFilter
所在的位置,则 ExceptionTranslatorFilter
不会看到您的过滤器抛出的任何可能的异常,而是会一直返回到 Tomcat 的 throwable()
的 org.apache.catalina.core.StandardHostValve
方法,(显然,如果您使用 Tomcat 作为 servlet 实现),它要做的是, 将您的请求重定向到默认错误路径,即 /error
。
这就是乐趣的开始;一旦过滤器抛出异常,该异常应该拦截进入应用程序的任何请求(例如
/hello
),现在将发出对预定义错误路径的后续请求(假设 /error
)。幸运的是,如果您没有碰巧使用 HttpSecurity
配置“允许”该路径,接下来将发生以下一系列事件:
/error
OncePerRequestFilter
(我认为会这样),它将不会再次被过滤,因为它在上一次传递中已被标记为已过滤。FilterSecurityInterceptor
AccessDecisionVoter
,这会立即将不熟悉的 /error
请求投票为未经身份验证,抛出 AccessDeniedException
并收工。ExceptionTranslationFilter
,将会看到这个 AccessDeniedException
,将其评估为常规访问拒绝,并调用 AuthenticationEntryPoint.commence
,其中 AccessDeniedException
是第三个参数。sendError
,并且您的客户端将收到此响应。那么这有什么问题,我在过滤器中阻止了请求,而客户端收到了错误响应?
问题是,客户端收到的消息不是为您的请求或错误生成的,而是为对
/error
路径的请求生成的,并且由 FilterSecurityInterceptor
生成了错误。
那么你应该做什么?
我谦虚地建议,将您的
JwtAuthTokenFilter
放在安全过滤器链中的最后两个过滤器之间,您可能抛出的错误将由默认的 Spring Security 错误解析器处理,并且您将为服务器节省 StandardHostValve
之间的往返时间
和 FilterSecurityInterceptor
。
http.addFilterAfter(authenticationJwtTokenFilter(), ExceptionTranslationFilter.class);
或者不这样做,客户端最终会收到错误。