Spring Security(二)安全架构与认证鉴权原理
1. Spring Security Servlet 安全架构
Spring Security 设计的 Servlet 安全从架构上分为三个层次,分别是「认证」、「鉴权」、「入侵防护」。通过过滤器机制将安全逻辑应用到 Servlet 项目。
请求的接收和处理是通过一个一个的过滤器顺序执行实现的,过滤器是 Servlet 项目处理请求的基础。
Spring 将自己体系内的过滤器交由「过滤器代理FilterChainProxy」管理,FilterChainProxy 同样也是一个过滤器,被封装在 Spring 的「过滤器委托代理DelegatingFilterProxy」中。
Spring Security 在 FilterChainProxy 中加入了「安全过滤器链SecurityFilterChain」实现安全保护功能。
其过程如图:

安全过滤器链(SecurityFilterChain)的特点:
- 为所有 Spring Security支持的Servlet指明了起点;
- 对于一些后台操作,可以提升执行效率;
- 在 Servlet容器中,过滤器的选择是由URL决定的,如此便可针对不同URL指定相互独立的安全策略。
1.1 安全过滤器 Filter
Spring Security 内置了 33 种安全过滤器,每个过滤器有固定的顺序及应用场景;内置过滤器的参数设置通过 HttpSecurity 类相应的配置方法完成。
在认证与授权中关键的三个过滤器:
- UsernamePasswordAuthenticationFilter:该过滤器用于拦截我们表单提交的请求(默认为/login),进行用户的认证过程。
- FilterSecurityInterceptor:该过滤器主要用来进行授权判断。
- ExceptionTranslationFilter:该过滤器主要用来捕获处理- spring security抛出的异常,异常主要来源于- FilterSecurityInterceptor。
Spring Security 的认证、授权异常在过滤器校验过程中产生,并在 ExceptionTranslationFilter 中接收并进行处理,
- ExceptionTranslationFilter过滤器首先像其他过滤器一样,调用过滤器链的执行方法- FilterChain.doFilter(request, response)启动过滤处理;
- 如果当前的用户没有通过认证或者因为其他原因在执行过程中抛出了 AuthenticationException 异常,此时将开启「认证流程」:
- 清空 SecurityContextHolder对象;
- 并将原始请求信息「request」保存到RequestCache对象中;
- 使用 AuthenticationEntryPoint对象存储的认证地址,向客户端索要身份证明。例如,使用浏览器登录的用户,将浏览器地址重定向到 /login 或者回传一个 WWW-Authenticate 认证请求头。
 
- 清空 
- 如果当前用户身份信息已确认,但是没有访问权限,则会产生 AccessDeniedException异常,然后访问被拒绝。继续执行拒绝处理AccessDeniedHandler。
1.2 自定义过滤器 Filter
在 HttpSecurity 对象中增加自定义 Filter 可用于实现认证方式的扩展等场景,扩展 Filter 需要实现 javax.servlet.Filter 接口;并且需要指定新过滤器的位置。
例如,扩展自定义接口 SimpleFilter。
- 自定义接口类
public class SimpleFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("In SimpleFilter");
    }
}
- 加入到指定位置,比如加在 UsernamePasswordAuthenticationFilter 之前
http.addFilterBefore(new SimpleFilter(), UsernamePasswordAuthenticationFilter.class);
2. Spring Security 认证
2.1 Spring Security 基本认证组件
| 组别 | 组件名 | 简述 | 
|---|---|---|
| 存储单元 | Authentication | 维护用户用于认证的信息 | 
| GrantedAuthority | 认证用户的权限信息比如角色、范围等等 | |
| SecurityContextHolder | 用于维护 SpringContext | |
| SecurityContext | 用来存储当前认证用户的信息 | |
| 认证管理 | AuthenticationManager | SpringSecurity 向外提供的用于认证的 API 集合 | 
| ProviderManager | AuthenticationManager 的常见实现类 | |
| AuthenticationProvider | 用于 ProviderManager 提供认证实现 | |
| AuthenticationEntryPoint | 用于获取用户认证信息 | |
| 流程管理 | AbstractAuthenticationProcessingFilter | 是认证过滤器的基础,用于组合认证流程 | 
2.2 存储单元

- SecurityContextHolder对象是整个- Spring Security体系的核心,它维护着- SecurityContext对象。它是唯一的。
- SecurityContext对象用于衔接- SecurityContextHolder和- Authentication对象,是对- Authentication的外层封装。
- Authentication是用户的认证信息。
Authentication对象有三个核心属性:
- principal:用户的身份信息;
- credentials:用户的认证凭据,比如密码,通常情况下,当用户完成认证后,此项内容就会被清空;
- authorities:用户的权限,用于更高层次的鉴权功能,通常包括角色、使用范围等信息。该属性基本由 GrantedAuthority实现。GrantedAuthority是在前述Authentication对象中所指的权限信息。在开发过程中,可以通过Authentication.getAuthorities()方法获取。权限信息通常包括角色、范围,或者其他扩展内容。
Authentication 两个主要作用:
- 为 AuthenticationManager对象提供用于认证的信息载体;
- 用于获取某个用户的基本信息。
2.3 认证管理
- AuthenticationManager为- Spring过滤器提供认证支持- API。- AuthenticationManager的实现形式并没有严格限制,通常情况下使用- ProviderManager。
- ProviderManager是- AuthenticationManager的最常用的实现类,它包含了一系列的- AuthenticationProvider对象,用以判断认证流程是否完成、认证结构是否成功。
- AuthenticationProvider:每个- ProviderManager可以包含多个- AuthenticationProvider,每个- AuthenticationProvider提供一种认证类型,例如:- DaoAuthenticationProvider可以完成「用户名 / 密码」的认证,- JwtAuthenticationProvider用于完成 JWT 方式的认证。
- AuthenticationEntryPoint在当一个请求包含的认证信息不全时,比如未认证终端访问受保护资源时发挥作用,如跳转到登录页面、返回认证要求等。
2.4 流程管理
AbstractAuthenticationProcessingFilter 是所有认证过滤器的基类。
- 当用户提交认证信息,AbstractAuthenticationProcessingFilter 首先从请求信息(例如用户名、密码)中创建 Authentication 对象;
- 将 Authentication 对象传递给 AuthenticationManager 对象,用于后续认证;
- 如果认证失败,则执行失败流程:
- 清空 SecurityContextHolder 对象;
- 触发 RememberMeServices.loginFail 方法;
- 触发 AuthenticationFailureHandler。
 
- 如果认证成功,则执行成功流程:
- SessionAuthenticationStrategy 登记新的登录;
- 将 Authentication 对象设置到 SecurityContextHolder 对象中,并将 SecurityContext 对象保持到 Session 中;
- 调用 RememberMeServices.loginSuccess 方法;
- ApplicationEventPublisher 发起事件 InteractiveAuthenticationSuccessEvent
 
3. Spring Security 鉴权
Spring Security 包含确认身份和确认身份的可执行操作两部分,前者为认证(Authentication),后者即为鉴权(Authorization);
3.1 权限
Spring Security 的权限默认是以字符串形式存储的权限信息,比如角色名称、功能名称等;
在用户身份信息得到确认后,Authentication 中会存储一系列的 GrantedAuthority 对象,这些对象用来判断用户可以使用哪些资源。
GrantedAuthority 对象通过 AuthenticationManager 插入到 Authentication 对象中,并被 AccessDecisionManager 使用,判断其权限。
GrantedAuthority 是一个接口,其仅包含一个 getAuthority() 方法,返回一个字符串值,该值作为权限的描述,当权限较为复杂,该方法需要返回 null,此时 AccessDecisionManager 会根据 getAuthority() 返回值情况判断是否要进行特殊处理。
SimpleGrantedAuthority 是 GrantedAuthority 的一个基础实现类,可以满足一般的业务需求。
3.2 鉴权
- 前置鉴权:由 AccessDecisionManager对象判断其是否允许继续执行; 权限判断发生在方法被调用前,或者 WEB 请求之前。不满足抛出AccessDeniedException异常。
- 后置鉴权:通过 AfterInvocationManager进行管理;后置鉴权在资源被访问后,根据权限的判定来修改返回的内容,或者返回AccessDeniedException。
前置鉴权 AccessDecisionManager 对象由 AbstractSecurityInterceptor 发起调用,其职责是给出资源是否能被访问的最终结果;
- AccessDecisionManager包含三个主要方法:- boolean supports(ConfigAttribute attribute);:判断配置属性是否可被访问;
- boolean supports(Class clazz);:判断安全对象的类型是否支持被访问;
- void decide(Authentication authentication, Object secureObject,Collection<ConfigAttribute> attrs) throws AccessDeniedException;:通过认证信息、安全对象、权限信息综合判断安全对象是否允许被访问。
 
Spring Security 内置了以「投票」为判定方法的鉴权策略。Spring Security 的鉴权策略可以由用户自己实现。
投票策略下,AccessDecisionManager 控制着一系列的 AccessDecisionVoter 实例,判断权限是否满足,如果不满足抛出 AccessDeniedException 异常。
- AccessDecisionVoter也包含三个方法:- boolean supports(ConfigAttribute attribute);:判断配置属性是否支持;
- boolean supports(Class clazz);:判断类型是否支持;
- int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attrs);:根据认证信息对安全资源进行投票。
 
投票鉴权分为三类:
- 基于角色的投票:RoleVoter;
- 基于认证信息的投票:AuthenticatedVoter,主要区分认证用户、匿名用户等;
- 自定义投票策略。
3.3 Servlet 请求鉴权流程
Servlet 鉴权主要围绕着 FilterSecurityInterceptor 类展开,该类作为一个安全过滤器,被放置在 FilterChainProxy 中。
具体流程如下:
- FilterSecurityInterceptor从- SecurityContextHolder中获取- Authentication对象;
- FilterSecurityInterceptor从- HttpServletRequest、- HttpServletREsponse、- FilterChain中创建- FilterInvocation对象;
- 将创建的 FilterInvocation对象传递给SecurityMetadataSource用来获取ConfigAttribute对象集合;
- 最后,将 Authentication、FilterInvocation和ConfigAttribute对象传递给AccessDecisionManager实例验证权限:- 如果验证失败,将抛出 AccessDeniedException异常,并由ExceptionTranslationFilter接收并处理;
- 如果验证通过,FilterSecurityInterceptor将控制权交还给FilterChain,使程序继续执行。
 
- 如果验证失败,将抛出 
相关系列文章
- Spring Security(五)前后端分离后台菜单权限控制
- Spring Security(四)基于Redis的Token自动续签优化
- Spring Security(三)整合JWT实现无状态登录示例
- Spring Security(二)安全架构与认证鉴权原理
- Spring Security(一)基础入门示例