1.简介
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
用户在进行资源访问时,要求系统要对用户进行权限控制,其具体流程如图所示:
3.SpringSecurity认证授权分析
基于Spring Security提供的过滤链,对Spring Security如何完成认证和授权的过程进行分析。
3.1 Servlet技术体系中的过滤链
我们先来看一下JAVA WEB应用中的Servlet过滤链(FilterChain),也是请求响应处理链,这个过滤链中包含Filter和Servlet对象。如图所示:
当客户端向Web服务器(例如Tomcat)发送请求时,Web服务器会为指定的URL请求创建一个过滤链,然后通过这个过滤链中的Servlet和Filter对象处理请求。Filter可在这个执行链中对请求进行预处理,也可以基于业务阻断这个请求继续传递。Servlet主要负责对请求进行分发,例如请求交给url对应的后端处理器进行处理。在我们熟知的Spring MVC 应用中,这个Servlet对象是DispatcherServlet。
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain) {
// do something before the rest of the application
chain.doFilter(request, response); // invoke the rest of the application
// do something after the rest of the application}
3.2 DelegatingFilterProxy
DelegatingFilterProxy是Spring提供的一个Filter接口的实现,目的是在Servlet容器(例如tomcat)和Spring容器(例如ApplicationContext)之间建立一座桥。Servlet 容器允许按照它的规范进行Filter对象的注册,但是它察觉不到Spring中定义的Bean对象。
DelegatingFilterProxy 可以按照Servlet容器规范注册到Servlet容器的过滤链中,然后把工作委托给Spring容器中的Filter Bean对象。如图所示:
DelegatingFilterProxy
对象可以从Spring容器中查找这些Filter类型的Bean对象,然后调用执行这些对象。我们可以看一段代码,例如:
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain) {
// Lazily get Filter that was registered as a Spring Bean
// For the example in DelegatingFilterProxy delegate
is an instance of Bean Filter0
Filter delegate = getFilterBean(someBeanName); // delegate work to the Spring Bean
delegate.doFilter(request, response);
}
DelegatingFilterProxy在查找Filter类型的Bean实例时,还可采用一定的延迟机制。这一点非常重要,因为Servlet容器默认会在启动前,注册所有的Filter实例。然而,Spring 使用
ContextLoaderListener对象负责加载Spring容器中的Bean,但前提条件是要先完成这些Bean的注册。
3.3 FilterChainProxy
FilterChainProxy对象是Spring Security安全框架提供的一个特殊的过滤器,由DelegatingFilterProxy对象装配。这个过滤器通过SecurityFilterChain对象将一些任务委派给Spring容器中的其它过滤器Bean对象。如图所示:
3.4 SecurityFilterChain
SecurityFilterChain包含很多认证授权过滤器,通过这些过滤器处理客户端请求。如图所示:
SecurityFilterChain 过滤器链中的所有Security Filters对象都是Bean对象,它们通过FilterChainProxy对象进行注册。同时FilterChainProxy对象还可以决定应该由哪个SecurityFilterChain对象去处理请求,如图所示:
3.5 AbstractAuthenticationProcessingFilter
此对象主要用于处理用户的认证请求,如图所示:
用户提交认证请求时, 此对象会基于认证请求创建Authentication对象,基于此对象封装需要认证的用户信息。然后再将这个对象交给AuthenticationManager进行认证。
3.6 表单方式的认证流程分析
用户去访问服务器中的私有资源时,假如过滤器链中的FilterSecurityInterceptor对象检测到用户还没有认证,则会抛出一个访问被拒绝的异常。这个异常对象可通过ExceptionTranslationFilter对象处理,发起一个重定向请求(/login),然后客户端基于get请求去访问/login资源,服务端返回一个登陆页面。如图:
客户端呈现登陆页面后,用户在登陆页面,提交用户名和密码信息,执行认证操作即可。用户在登陆表单中填写用户名和密码,然后点击登陆按钮提交用户信息,此时SpringSecurity安全框架底层会通过UsernamePasswordAuthenticationFilter对象对用户密码进行认证,如图所示:
UsernamePasswordAuthenticationFilter对象会基于用户提交账号信息创建一个UsernamePasswordAuthenticationToken对象(Authentication类型),然后将此对象交给AuthenticationManager对象(认证管理器)进行认证。
3.7 HTTP请求授权过程分析
用户在进行资源访问时,SpringSecurity会基于 FilterSecurityInterceptor对象对请求进行拦截,然后检测当前请求中的用户是否已经认证,是否有权限访问这个资源。如图所示:
FilterSecurityInterceptor对象拦截到用户请求后会从SecurityContextHolder对象中获取认证对象,然后创建FilterInvocation对象,并通过此对象获取一些配置属性,最后通过AccessDecisionManager对象检查用户是否有权限访问资源,假如有权限,则放行用户对资源的访问。假如没有权限,则抛出AccessDeniedException异常。
3.8 ExceptionTranslationFilter
ExceptionTranslationFilter过滤器执行时会对异常进行捕获,并从异常堆中提取SpringSecurityException异常对象,然后基于具体的异常类型,调用不同的异常处理器对象进行处理。如图所示:
图中的Security Exception异常类型假如是AuthenticationException类型,可以调用AutenticationEntryPoint对象处理异常。假如是AccessDeniedException类型,则可以调用AccessDeniedHandler对象处理异常。
4.总结:
通过对Spring Security 框架中的过滤链分析,介绍了此框架完成用户身份认证,授权,异常处理的过程。比方说通过UsernamePasswordAuthenticationFilter 过滤器拦截客户端提交的登录表单请求,并基于请求中的信息进行用户身份认证。然后通过FilterSecurityInterceptor 来判断当前请求是否有权限访问对应的资源。如果没有认证或访问受限会抛出相关异常,并由 ExceptionTranslationFilter 过滤器进行捕获和处理。
参考
https://docs.spring.io/spring-security/reference/servlet/index.html