Spring Security
Spring Security 是一个提供认证、授权以及一些常见漏洞防护的框架。该框架为 Servlet 应用程序(Spring MVC)和响应式应用程序(Spring WebFlux,本文不表)提供防护,并作为防护 spring 项目的事实标准。
Servlet 应用程序
Spring Security 通过标准的 Servlet 过滤器
集成 Servlet 容器。这表明只要在 Servlet 容器中运行的应用程序都可以使用该框架。具体来说,你不需要在以 Servlet 为容器的应用程序中专门引入 Spring,就可以使用该框架。
在 Spring Boot 项目中使用 Spring Security
新建项目
build.gradle 中输入以下内容:
运行
控制台输出
Spring Boot Auto Configuration
Spring Boot 自动处理以下内容:
启用 Spring Security 的默认配置,它会创建一个 servlet
过滤器
bean,叫做springSecurityFilterChain
。这个 bean 负责应用程序中的所有安全工作(保护应用程序的 URL,验证提交的用户名和密码,重定向登录表单,等等)。用户名(username)为
user
,密码为 console 中生成的随机密码,使用此账号和密码创建UserDetailsService
bean。通过 Servlet 容器为每个请求注册名为
springSecurityFilterChain
的过滤器
。
Spring Boot 配置的内容不多,但是做的很多。功能总结如下:
与应用程序的任何交互都需要经过身份验证的用户
为你生成默认登录表单
使用用户名
user
和在 console 中生成的密码,基于表单做身份认证(在刚才 console 中输出的密码为:fceb50df-05cf-40e4-a4c5-67d5da55a6b4
)通过 BCrypt 加密密码,以此保护密码
用户可以退出登录
防止 CSRF(Cross-site request forgery,跨站请求伪造) 攻击
Session Fixation 防护
集成安全头用于防护请求的 HTTP Strict Transport Security 集成 X-Content-Type-Options 控制缓存(你的应用程序可以重写缓存,从而缓存你的静态资源)集成 X-XSS-Protection 集成 X-Frame-Options 以阻止 Clickjacking
集成以下 Servlet API:HttpServletRequest#getRemoteUser()HttpServletRequest.html#getUserPrincipal()HttpServletRequest.html#isUserInRole(java.lang.String)HttpServletRequest.html#login(java.lang.String, java.lang.String)HttpServletRequest.html#logout()
Spring Security 在 Servlet 应用程序中的顶层架构
我们将基于认证、授权、Protection Against Exploits 来理解这种顶层架构。
过滤器
Spring Security 的 Servlet 建立在 Servlet 的 过滤器
上,所以先查看 过滤器
的作用很有帮助。以下图片显示了单个请求的处理层级。
客户端给应用程序发送请求,容器创建一个包含过滤器
和 Servlet
的 FilterChain
,它将处理基于请求 URI 路径的 HttpServletRequest
。在 Spring MVC 应用程序中,Servlet
是 DispatcherServlet
的实例。一个 Servlet
最多只能处理一个 HttpServletRequest
和 HttpServletReponse
。不过,可以使用多个 过滤器
处理以下内容:
阻止下游
过滤器
或者Servlet
被调用。在这个实例中,过滤器
将写HttpServletReponse
。修改下游
过滤器
和Servlet
的HttpServletRequest
和HttpServletReponse
。
FilterChain
的能力来自于传入其中的 过滤器
。
FilterChain 用例
因为 过滤器
只影响下游的 过滤器
和 Servlet
,所以每个 过滤器
的调用顺序及其重要。
DelegatingFilterProxy
Spring 提供一个叫做 DelegatingFilterProxy 的实现,它桥接了 Servlet 容器的生命周期和 Spring 的 ApplicationContext
。Servlet 容器允许其使用自己的标准注册 过滤器
,但是它感应不到 Spring 定义的 bean。DelegatingFilterProxy
可以凭借 Servlet 容器的机制完成注册,但是所有的工作都委托给实现了 过滤器
的 Spring Bean。
以下是 DelegatingFilterProxy
如何融合 过滤器
和 FilterChain
的图。
DelegatingFilterProxy
从 ApplicationContext
中查找并调用 Filter0 Bean。DelegatingFilterProxy
的伪代码如下。
DelegatingFilterProxy
的另一个好处是可以延迟查找 过滤器
的实例。这种方式十分重要,原因是容器需要在启动之前注册 过滤器
。不过,Spring 一般通过 ContextLoaderListener
加载 Spring 的 Bean,直到所有的 过滤器
注册完才结束。
FilterChainProxy
对 Spring Security 的 Servlet 支持,包含在 FilterChainProxy 中。FilterChainProxy
是 Spring Security 提供的一种特殊 过滤器
,它可以通过 SecurityFilterChain
委托很多 过滤器
。因为 FilterChainProxy
是一个 Bean,所以它一般封装在 DelegatingFilterProxy
中。
SecurityFilterChain
SecurityFilterChain
被 FilterChainProxy
用于决定在该请求种哪个 Spring Security 的过滤器应该被调用。
Security Filters 在 SecurityFilterChain
一般以 Bean 的方式存在,它们通过 FilterChainProxy
注册,从而替代 DelegatingFilterProxy
。FilterChainProxy
直接通过 Servlet 容器或 DelegatingFilterProxy 注册,提供了诸多好处。首先,它为 Spring Security 的 Servlet 提供支撑起点。因此,如果你要解决 Spring Security Servlet 的问题,在 FilterChainProxy
中添加 debug 断点是不错的开始。
第二,由于 FilterChainProxy
是使用 Spring Security 的重点,它可以执行一些必要的任务。例如,清除 SecurityContext
,从而避免内存泄漏。还可以应用于 Spring Security 的 HttpFirewall,从而保护应用程序免受某些类型的攻击。
此外,在决定何时调用 SecurityFilterChain
时,它提供了更多的灵活性。在 Servlet 容器中,只基于 URL 调用 过滤器
。不过,FilterChainProxy
通过利用 RequestMatcher
接口,可以根据 HttpServletRequest
中的任何事情决定调用。
事实上,FilterChainProxy
可以用于决定应该调用哪个 SecurityFilterChain
。因此可以为应用程序的不同切片提供一个总体的分隔配置。
在上图中,FilterChainProxy
决定应该使用哪个 SecurityFilterChain
。第一个匹配的 SecurityFilterChain
会被调用。如果发起一个 URL 为 /api/message
的请求,它将匹配 SecurityFilterChain 0
的模式 /api/**
,所以只会调用 SecurityFilterChain 0
,即使也匹配 SecurityFilterChain n
。如果发起一个 URL 为 /messages
的请求,它不匹配 SecurityFilterChain 0
的模式 /api/**
,所以 FilterChainProxy
将继续尝试每一个 SecurityFilterChain
。如果没有匹配到,SecurityFilterChain
匹配的 SecurityFilterChain n
实例将会被调用。
注意,SecurityFilterChain 0
只配置了 3 个安全 过滤器
。但是,SecurityFilterChain n
配置了 4 个 过滤器
实例。每个 SecurityFilterChain
可以孤立的配置,留意这一点很重要。事实上,SecurityFilterChain
可能不配置 过滤器
,如果 Spring Security 要忽略某些请求。
Security Filters
Security Filters 通过 SecurityFilterChain
API 插入到 [[#FilterChainProxy]] 中。[[#过滤器]] 的顺序十分重要。通常,无需了解 Spring Security 过滤器
的顺序。但是,有些时候了解它们排序是有益的。
以下是 Spring Security 过滤器的综合排序:
ChannelProcessingFilter
SecurityContextPersistenceFilter
WebAsyncManagerIntegrationFilter
HeaderWriterFilter
CorsFilter
CsrfFilter
LogoutFilter
OAuth2AuthorizationRequestRedirectFilter
Saml2WebSsoAuthenticationRequestFilter
X509AuthenticationFilter
AbstractPreAuthenticatedProcessingFilter
CasAuthenticationFilter
OAuth2LoginAuthenticationFilter
Saml2WebSsoAuthenticationFilter
OpenIDAuthenticationFilter
DefaultLoginPageGeneratingFilter
DefaultLogoutPageGeneratingFilter
ConcurrentSessionFilter
BearerTokenAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
JaasApiIntegrationFilter
RememberMeAuthenticationFilter
AnonymousAuthenticationFilter
OAuth2AuthorizationCodeGrantFilter
SessionManagementFilter
SwitchUserFilter
处理 Security 异常
ExceptionTranslationFilter
可以将 AccessDeniedException
和 AuthenticationException
转入 HTTP 响应中。
ExceptionTranslationFilter
作为 Security Filters 中一员插入到 [[#FilterChainProxy]] 中。
首先,
ExceptionTranslationFilter
调用Filter.doFilter(requeset,response)
以执行其余应用程序。如果用户未认证或者是
AuthenticationException
,那么开始认证。清除SecurityContextHolder
。HttpServletRequest
保存在RequestCache
中。当用户成功认证后,RequestCache
用于重现原始请求。AuthenticationEntryPoint
用于来自客户端的请求凭证。例如,重定向到登录页面或者发送WWW-Authenticate
头文件。另外,如果是
AccessDenieException
,那么将拒绝访问。将唤起AccessDeniedHandler
处理拒绝访问。
ExceptionTranslationFilter
的伪代码如下:
版权声明: 本文为 InfoQ 作者【Zhang】的原创文章。
原文链接:【http://xie.infoq.cn/article/44fb657710309751096cf075a】。文章转载请联系作者。
评论