Spring Secutity和Apache Shiro是Java领域的两大主流开源安全框架,也是权限系统设计的主要技术选型。本文主要介绍Spring Secutity的实现原理,并基于Spring Secutity设计基于RBAC的权限系统。
一、技术选型为何把Spring Secutity作为权限系统的技术选型,主要考虑了以下几个方面:
数据鉴权的能力:Spring Secutity支持数据鉴权,即细粒度权限控制。
Spring生态基础:Spring Secutity可以和Spring生态无缝集成。
多样认证能力:Spring Secutity支持多样认证方式,如预认证方式可以与第三方认证系统集成。
Spring Security Apache Shiro认证 支持多种认证方式(如密码、匿名、预认证) 简单登录认证
鉴权 功能鉴权、数据鉴权 功能鉴权
多源适配 Mem、JDBC、DAO、LDAP、
OpenID、OAuth等 LDAP、JDBC、Kerberos、
ActiveDirectory等
加密 支持多种加密方式 简单加密方式
运行环境 依赖Spring 可独立运行
开放性 开源、Spring生态基础 开源
复杂度 复杂、较重 简单、灵活
二、核心架构
权限系统一般包含两大核心模块:认证(Authentication)和鉴权(Authorization)。
认证:认证模块负责验证用户身份的合法性,生成认证令牌,并保存到服务端会话中(如TLS)。
鉴权:鉴权模块负责从服务端会话内获取用户身份信息,与访问的资源进行权限比对。
官方给出的Spring Security的核心架构图如下:
核心架构解读:
AuthenticationManager:负责认证管理,解析用户登录信息(封装在Authentication),读取用户、角色、权限信息进行认证,认证结果被回填到Authentication,保存在SecurityContext。
AccessDecisionManager:负责鉴权投票表决,汇总投票器的结果,实现一票通过(默认)、多票通过、一票否决策略。
SecurityInterceptor:负责权限拦截,包括Web URL拦截和方法调用拦截。通过ConfigAttributes获取资源的描述信息,借助于AccessDecisionManager进行鉴权拦截。
SecurityContext:安全上下文,保存认证结果。提供了全局上下文、线程继承上下文、线程独立上下文(默认)三种策略。
Authentication:认证信息,保存用户的身份标示、权限列表、证书、认证通过标记等信息。
SecuredResource:被安全管控的资源,如Web URL、用户、角色、自定义领域对象等。
ConfigAttributes:资源属性配置,描述安全管控资源的信息,为SecurityInterceptor提供拦截逻辑的输入。
三、设计原理通过对源码的分析,我把Spring Security的核心领域模型设计整理如下:
全局抽象模型解读:
配置:AuthenticationConfiguration负责认证系统的全局配置,GlobalMethodSecurityConfiguration负责方法调用拦截的全局配置。
构建:AuthenticationConfiguration通过AuthenticationManagerBuilder构建认证管理器AuthenticationManager,GlobalMethodSecurityConfiguration会自动初始化AbstractSecurityInterceptor进行方法调用拦截。
Web拦截:HttpSecurity对Web进行安全配置,内置了大量GenericFilterBean过滤器对URL进行拦截。负责认证的过滤器会通过AuthenticationManager进行认证,并将认证结果保存到SecurityContext。
方法拦截:Spring通过AOP技术(cglib/aspectj)对标记为@PreAuthorize、@PreFilter、@PostAuthorize、@PostFilter等注解的方法进行拦截,通过AbstractSecurityInterceptor调用AuthenticationManager进行身份认证(如果必要的话)。
认证:认证管理器AuthenticationManager内置了多种认证器AuthenticationProvider,只要其中一个认证通过,认证便成功。不同的AuthenticationProvider获取各自需要的信息(HTTP请求、数据库查询、远程服务等)进行认证,认证结果全部封装在Authentication。需要加载用户、角色、权限信息的认证器(如密码认证、预认证等)需要对接UserDetailsManager接口实现用户CRUD功能。