Spring Security的整体架构
- 一、整体架构
- 认证(Authentication)
- AuthenticationManager
- Authentication
- 登录后的数据保存(SecurityContextHolder)
- 授权(Authorization)
- ConfigAttribute
- 二、总结
这篇博客所述主要是在读《深入浅出Spring Security》途中所做的笔记(之前有学Spring Security,但了解的比较浅,所以想着看这本书深入一点点,这都是因为上次一个bug调了我几天)
这本书的 pdf 网盘链接可通过微信扫下方公众号私信”深入浅出Spring Security”即可获取。
一、整体架构
在 SpringSecurity 的架构设计中,认证(Authentication)和授权(Authorization)是分开的,无论使用什么样的认证方式,都不会影响授权,这是两个独立的存在,这种独立带来的好处之一,就是可以非常方便地整合一些外部的解决方案。下图是认证和授权所用到的核心接口(下面会分别进行解释):
认证(Authentication)
通俗地说,认证就是身份认证(你是谁?)
AuthenticationManager
在SpringSecurity中认证是由 AuthenticationManager
接口来负责的,接口定义为:
public interface AuthenticationManager {Authentication authenticate(Authentication authentication) throws AuthenticationException;}
- 返回
Authentication
表示认证成功; - 返回
AuthenticationException
异常,表示认证失败。
AuthenticationManager 主要实现类为 ProviderManager,在 ProviderManager 中管理了众多AuthenticationProvider 实例。在一次完整的认证流程中,SpringSecurity 允许存在多个AuthenticationProvider,用来实现多种认证方式,这些 AuthenticationProvider 都是由 ProviderManager
进行统一管理的。
Authentication
认证以及认证成功的信息主要是由 Authentication 的实现类进行保存的,Authentication 接口的结构如下:
- getAuthorities:获取用户权限信息;
- getCredentials:获取用户凭证信息,一般指密码;
- getDetails:获取用户详细信息;
- getPrincipal:获取用户身份信息,用户名、用户对象等;
- isAuthentiacted:用户是否认证成功
登录后的数据保存(SecurityContextHolder)
SecurityContextHolder 用来获取登录之后的用户信息。SpringSecurity 会将登录用户数据保存在 Session 中。但是,为了使用方便,SpringSecurity 在此基础上做了一些改进,其中最主要的一个变化就是线性绑定。当用户登录成功后,SpringSecurity 会将登录成功的用户信息保存到 SecurityContextHolder 中。SecurityContextHolder 中的数据保存默认是通过 ThreadLocal 来实现的,使用 ThreadLocal 创建的变量只能被当前线程访问,不能被其他线程访问和修改,也就是用户数据和请求线程绑定在一起。
当登录请求处理完毕后,SpringSecurity 会将SecurityContextHolder 中的数据拿出来保存到session中,同时将 SecurityContextHolder 中的数据清空。以后每当有请求到来时,SpringSecurity 就会先从 Session 中取出用户登录数据,保存到 SecurityContextHolder 中,方便在该请求的后续处理过程中使用,同时在请求结束时将 SecurityContextHolder 中的数据拿出来保存到 Session 中,然后将Security 的 SecurityContextHolder 中的数据清空。这一策略非常方便用户在 Controller、Service 层以及任何代码中获取当前登录用户数据。
其实它就是为了方便我们拿用户信息数据,免得每次都得从 Session 会话中拿,从Session会话中拿,不好的地方就是要进行 SessionID 的判断,封装到SecurityContextHolder中就减少了判断这一步,直接获取即可。
授权(Authorization)
通俗点说,授权就是访问控制(你可以做什么?)
当完成认证后,接下来就是授权了。在SpringSecurity 的授权体系中,有两个关键的接口:
AccessDecisionManager:访问决策管理器,用来决定此次访问是否被允许。
public interface AccessDecisionManager {void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)throws AccessDeniedException, InsufficientAuthenticationException;boolean supports(ConfigAttribute attribute);boolean supports(Class<" />> clazz);}
AccessDecisionVoter:访问决定投票器,投票器会检查用户是否具备应有的角色,进而投出赞成、反对或者弃权票。voter(选举人)
public interface AccessDecisionVoter<S> {int ACCESS_GRANTED = 1;// granted:赞同int ACCESS_ABSTAIN = 0;// abstain:弃权int ACCESS_DENIED = -1;// denied:反对,否认boolean supports(ConfigAttribute attribute);boolean supports(Class<?> clazz);int vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);}
AccessDecisionVoter 和 AccessDecisionManager 都有众多的实现类,在 AccessDecisionManager 中会挨个遍历 AccessDecisionVoter,进而决定是否允许用户访问,因而 AccessDecisionVoter 和 AccessDecisionManager 两者的关系类似于 AuthenticationProvider 和 ProviderManager 之间的关系。
ConfigAttribute
在 AccessDecisionVoter 接口下的 vote 方法中,第三个参数是一个泛型参为ConfigAttribute 的Collection 集合对象。它你可以理解为是角色全称字符串的集合,内部就一个getAttribute方法。
public interface ConfigAttribute extends Serializable {String getAttribute();}
在 Spring Security 中,用户请求一个资源(通常是一个网络接口或者一个Java方法)所需要的角色会被封装成一个 ConfigAttribute 对象,在 ConfigAttribute 中只有一个 getAttribute 方法,该方法返回一个 String 字符串,就是角色的名称,角色名称都带有一个 ROLE_ 前缀,投票器 AccessDecisionVoter 所做的事情,其实就是比较用户所具备的角色和请求某个资源所需的 ConfigAttribute 之间的关系。
二、总结
了解 Spring Security 的整体架构,主要是为了后续了解其内部原理做准备。Spring Security 作为一个安全框架,其主要就是认证和授权两大结构。
认证(Authentication)的用户信息是间接的保存在 SecurityContextHolder
中,在认证管理(AuthenticationManager
)中管理着多个认证执行器,由它们进行认证,认证的有关详情被封装在Authenticatoin
中。
授权(Authorization)中权限名可通过ConfigAttribute
通过getAttribute
方法进行获取,通过AccessDecisionManager
判断访问是否允许,而判断的依据是AccessDecisionVoter
的投票vote
方法的结果。