百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

SpringSecurity原理剖析与权限系统设计

toyiye 2024-06-21 19:08 11 浏览 0 评论

Spring Secutity和Apache Shiro是Java领域的两大主流开源安全框架,也是权限系统设计的主要技术选型。本文主要介绍Spring Secutity的实现原理,并基于Spring Secutity设计基于RBAC的权限系统。

一、技术选型

为何把Spring Secutity作为权限系统的技术选型,主要考虑了以下几个方面:

  1. 数据鉴权的能力:Spring Secutity支持数据鉴权,即细粒度权限控制。
  2. Spring生态基础:Spring Secutity可以和Spring生态无缝集成。
  3. 多样认证能力:Spring Secutity支持多样认证方式,如预认证方式可以与第三方认证系统集成。

二、核心架构

权限系统一般包含两大核心模块:认证(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功能。
  • 鉴权:权限拦截器AbstractSecurityInterceptor通过读取不同的SecurityMetadataSource加载需要被鉴权资源的描述信息ConfigAttribute,然后把认证信息Authentication、资源描述ConfigAttribute、资源对象本身传递给AccessDecisionManager进行表决。AccessDecisionManager内置了多个投票器AccessDecisionVoter,投票器会将鉴权信息中的ConfigAttribute转换为SpringEL的格式,通过表达式处理器SecurityExpressionHandler执行基于表达式的鉴权逻辑,鉴权逻辑会通过反射的方式转发到SecurityExpressionRoot的各个操作上去。
  • 定制:通过WebSecurityConfigureAdapter可以定制HTTP安全配置HttpSecurity和认证管理器生成器AuthenticationManagerBuilder;通过AbstractPreAuthenticatedProcessingFilter可以定制预认证过滤器;通过UserDetailsManager和UserDetails接口可以对接自定义数据源;通过GrantedAuthority定制权限信息;通过PermissionEvaluator可以定制自定义领域模型的访问控制逻辑。

四、应用集成

理清Spring Security的定制点后,就可以在系统内部集成Spring Security了。

这里使用预认证的方式,以适配第三方认证系统。AbstractPreAuthenticatedProcessingFilter提供了预认证的扩展点,基于该抽象类实现一个自定义认证过滤器。

public class MyPreAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
 @Override
 protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
 // 从第三方系统获取用户ID
 return userId;
 }
 @Override
 protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
 return "";
 }
}

Spring Security会根据预认证过滤器getPreAuthenticatedPrincipal返回的用户ID信息,加载用户角色等初始信息。这里需要实现UserDetailsManager接口,提供用户信息管理器。

@Service
public class MyUserManager implements UserDetailsManager {
 @Override
 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
 // 从数据库加载用户信息
 return user;
 }
 
 // 其他管理接口
}

UserDetails内包含了GrantedAuthority接口类型的权限信息抽象,一般可以基于它自定义角色和权限。Spring Security使用一种接口形式表达角色和权限,角色和权限的差别是角色的ID是以"ROLE_"为前缀。

public class MyRole implements GrantedAuthority {
 private final String role;
 @Override
 public String getAuthority() {
 return "ROLE_" + role;
 }
}
public class MyAuthority implements GrantedAuthority {
 private final String authority;
 @Override
 public String getAuthority() {
 return authority;
 }
}

接下来注册自定义认证过滤器和用户管理器,这里需要实现WebSecurityConfigurerAdapter进行Web安全配置。

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.PROXY)
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
 @Autowired
 UserDetailsManager userDetailsManager;
 @Bean
 protected AuthenticationProvider createPreAuthProvider() {
 // 注册用户管理器
 PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
 provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsManager));
 return provider;
 }
 @Override
 protected void configure(HttpSecurity http) throws Exception {
 // 注册预认证过滤器
 http.addFilter(new MyPreAuthFilter(authenticationManager()));
 }
}

这样,最简单的Spring Security框架集成内系统内部已经完成了。在系统的任意服务接口上可以使用如下方式进行鉴权。

public interface MyService {
 @PreAuthorize("hasAuthority('QUERY')")
 Object getById(String id);
 
 @PreAuthorize("hasRole('ADMIN')")
 void deleteById(String id);
}

PreAuthorize注解表示调用前鉴权,Spring使用默认使用动态代理技术生成鉴权逻辑。注解内配置了SpringEL表达式来定制鉴权方式。上述代码中,hasAuthority会检查用户是否有QUERY权限,hasRole会检查用户是否有ADMIN角色。

使用动态代理的方式进行AOP,只允许在接口层面进行权限拦截,如果想在任意的方法上进行权限拦截,那么就需要借助于AspectJ的方式进行AOP。首先将注解EnableGlobalMethodSecurity的mode设置为AdviceMode.ASPECTJ,然后添加JVM启动参数,这样就可以在任意方法上使用Spring Security的注解了。

-javaagent:/path/to/org/aspectj/aspectjweaver/1.9.4/aspectjweaver-1.9.4.jar

以上还是只是以用户的身份信息(角色/权限)进行权限,灵活度有限,也发挥不了Spring Security的数据鉴权的能力。要使用数据鉴权,需要实现一个Spring Bean。

@Component
public class MyPermissionEvaluator implements PermissionEvaluator {
 @Override
 public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
 // 自定义数据鉴权
 return false;
 }
 @Override
 public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
 // 自定义数据鉴权
 return false;
 }
}

PermissionEvaluator会被自动注册到Spring Security框架,并允许在注解内使用如下方式进行鉴权。

@PreAuthorize("hasPermission(#id, 'QUERY')")
Object func1(String id) {
}
@PreAuthorize("hasPermission(#id, 'TABLE', 'QUERY')")
Object func2(String id) {
}

其中,func1的注解表示校验用户是否对id有QUERY权限,代码逻辑路由到MyPermissionEvaluator的第一个接口。func2的注解表示校验用户是否对TABLE类型的id有QUERY权限,代码逻辑路由到MyPermissionEvaluator的第二个接口。PermissionEvaluator提供了权限系统中数据鉴权的扩展点,稍后会描述如何利用该扩展点定制基于RBAC的权限系统。

五、权限系统

构建基于RBAC(Role Based Access Control)的权限系统,需要明确用户、角色、权限、资源这几个核心的概念类的含义和它们之间的关系。

  • 资源:权限系统内需要安全控制的客体,一般是系统内的数据或功能。
  • 权限:描述了资源上的操作抽象,一般是一种动作。
  • 授权:是权限和资源的组合,表示对资源的某一个操作。
  • 角色:描述了一组授权的集合,表示一类特殊概念的功能集。
  • 用户:权限系统的主体,一般是当前系统的访问用户,用户可以拥有多种角色。

以下是我们设计的基于RABC的权限核心领域模型:

一般情况下,系统内需要权限管控的资源是无法用户自定义的,因为资源会耦合大量的业务逻辑,所以我们提供了自 资源工厂,通过配置化的方式构建业务模块所需的资源。而用户、角色、权限,以及授权记录都是可以通过相应的管理器进行查询更新。

另外,资源抽象允许表达资源的继承和组合关系,继而表达更复杂的资源模型,资源统一鉴权的流程为:

  • 执行鉴权时,首先看资源是原子资源还是组合资源。
  • 对于原子资源,先查询是否有授权记录,再查看角色预授权是否包含当前授权,存在一种便成功。
  • 没有授权记录和角色预授权的原子资源,尝试用父资源(如果有的话)代替鉴权,否则鉴权失败。
  • 对于组合资源,先进行资源展开,获取子资源列表。
  • 遍历子资源列表,并依次对子资源进行鉴权,子资源鉴权结果汇总后,即组合资源鉴权结果。

综上,基于统一资源抽象和资源配置化构建,可以实现资源的统一构建,继而实现统一鉴权。

六、总结回顾

本文从Spring Security的架构和原理出发,描述了开源安全框架对于认证和鉴权模块的设计思路和细节。并提供了系统内集成Spring Security的方法,结合RBAC通用权限系统模型,讨论了统一资源构建和统一鉴权的设计和实现。如果你也需要设计一个新的权限系统,希望本文对你有所帮助。

相关推荐

# Python 3 # Python 3 教程(#python3.10教程)

Python3教程Python的3.0版本,常被称为Python3000,或简称Py3k。相对于Python的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python3....

Python教父推荐:《Python基础教程》(第3版)

《Python基础教程第3版》包括Python程序设计的方方面面:首先,从Python的安装开始,随后介绍了Python的基础知识和基本概念,包括列表、元组、字符串、字典以及各种语句;然后循序渐进地...

今日精选5篇教程:用Python3带你从小白入门机器学习实战教程手册

正文1:教程标题:英伟达SuperSloMoGithub项目开放作者:英伟达教程摘要:今年6月份,英伟达发布了一份生成高质量慢动作视频的论文——《SuperSloMo:HighQual...

电子书 | 笨办法学 Python 3(笨办法学python3pdf)

本周更新了5本IT电子书资源,同时站内已经有12本Python入门方面的相关电子书,可供新手选择。1、笨办法学Python3本书是一本Python入门书,适合对计算机了解不多,没有...

Python2 已终结,入手Python 3,你需要这30个技巧

选自medium作者:Erik-JanvanBaaren机器之心编译参与:王子嘉、一鸣Python2在今年和我们说拜拜了,Python3有哪些有趣而又实用的技巧呢?这篇教程有30个你会喜欢...

Python 3 系列教程(python3.9基础教程)

Python的3.0版本,常被称为Python3000,或简称Py3k。相对于Python的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python3.0在设计的时候没有考...

Python第三课3. Python 的非正式介绍

3.Python的非正式介绍?在下面的例子中,通过提示符(>>>与...)的出现与否来区分输入和输出:如果你想复现这些例子,当提示符出现后,你必须在提示符后键入例子中的每...

如何使用 Python 构建一个“谷歌搜索”系统?| 内附代码

来源|hackernoon编译|武明利,责编|Carol出品|AI科技大本营(ID:rgznai100)在这篇文章中,我将向您展示如何使用Python构建自己的答案查找系统。基本上,这...

Python 模拟微博登陆,亲测有效!(如何用python爬微博)

今天想做一个微博爬个人页面的工具,满足一些不可告人的秘密。那么首先就要做那件必做之事!模拟登陆……代码是参考了:https://www.douban.com/note/201767245/,我对代码进...

Python 驱动的 AI 艺术批量创作: 免费的Bing 绘图代码解析

这篇文章将深入分析一段Python代码,该代码利用Bing的AI绘图功能,即bing的images/create,根据用户提供的文本提示生成图像。我们将详细探讨其工作原理、代码结构、...

Python爬虫Scrapy库的使用入门?(python scrapy爬虫)

Scrapy是一个开源的并且支持高度可扩展的Python爬虫框架,主要被用来实现从网站提取数据。出现之初就是为网页抓取而设计,但是现在它也可以被用于从APIs中抓取数据或通用的Web抓取任务。Sc...

Python3 标准库概览(python标准库有什么)

操作系统接口os模块提供了不少与操作系统相关联的函数。>>>importos>>>os.getcwd()#返回当前的工作目录'C:\\Python34...

零基础入门学习Python(三):变量和字符串

分享兴趣,传播快乐,增长见闻,留下美好!亲爱的您,这里是LearningYard新学苑。今天小编为大家带来的是...

Python读写docx文件(python读写word)

Python读写docx文件Python读写word文档有现成的库可以处理pipinstallpython-docx安装一下。https://python-docx.readthedocs.io/...

如何利用Xpath抓取京东网商品信息

前几小编分别利用Python正则表达式和BeautifulSoup爬取了京东网商品信息,今天小编利用Xpath来为大家演示一下如何实现京东商品信息的精准匹配~~HTML文件其实就是由一组尖括号构成的标...

取消回复欢迎 发表评论:

请填写验证码