如何实现高效的动态鉴权

news2024/11/18 21:24:27

 一、概述

Spring Security 是 Spring 框架内高度可定制化的安全框架,也是 Spring 应用的标准安全框架,提供了包括认证和鉴权在内的两大部分。其高度集成于 Spring 框架,无需引入第三方扩展模块,可以避  免大量的数据接口适配问题,大幅度减少开发成本和时间。

如下图所示,Spring Security 的认证鉴权过程实际上位于请求过滤器和拦截器中,在请求通过了所有的过滤器和拦截器之后才会进行 API 适配。换言之,定制 Spring Security 就是修改过滤链中的各种过滤器和拦截器。

认证过程是图中绿色的部分,Spring Security 提供了非常多的认证方式,如密码认证、预认证等;橙色的部分是动态鉴权部分,其内置的 Security Interceptor 会将请求委托给各个具体的 AccessDecisionManager 进行鉴权。

1.jpg

Spring Security 的整个数据流程

在 Spring Security 中,AccessDecisionManager 是以投票器为蓝本进行的鉴权:Manager 下面会有多个 AccessDecisionVoter,每个 Voter 将结果返回至 Manager,最后由 Manager 确定是否授予权限。

Manager 共有三类:

  • AffirmativeBased 一票通过制(默认选项)

  • UnanimousBased 一票反对制

  • ConsensusBased 少数服从多数制

之所以会用投票器,一是方便添加和扩展 voters,二是量化鉴权结果简化框架实现。

二、鉴权模块构建

根据概述部分,我们可修改的地方有很多,例如 FilterSecurityInterceptor, AccessDecisionManager, AccessDecisionVoter。但是无论修改 Interceptor 还是 Manager 都非常花费时间,因此我们选择直接添加一个新的 Voter 来进行动态鉴权。

http.authorizeRequests().withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>(){
  @Override
  public < 0 extends FilterSecurityInterceptor > 0 postProcess(0 o){
     o.setAccessDecisionManager(new AffirmativeBased(Arrays.asList(new WebExpressionVotor(),userAccessDecisionVoter)));//决策管理器
     o.setSecurityMetadataSource(userFilterInvocationSecurityMetadataSource);//安全元数据
     return o;
  }
}};

在这里我们使用 AffirmativeBased 投票器进行投票,是因为进行自定义过滤的过程中,我们并没有包含 Spring 默认的属性,因此 WebExpressionVoter 会自动弃权,剩余步骤自然由我们自己的投票器 UserAccessDecisionVoter 进行投票和鉴权。

@Component
public class UserAccessDecisionVoter implements AccessDecisionVoter<FilterInvocation> { 
  @Override 
  public boolean supports(ConfigAttribute attribute) { 
     return true; 
  } 
                                    
  @Override 
  public boolean supports(Class<?> clazz) { 
    return true; 
  } 
                                                                             
  @Autowired
  AnalysisUserRoleUtil analysisUserRoleUtil; 
                                                                                             
  @Autowired
  JwtConfig jwtConfig;

  /**         
  * @param authentication   用户信息         
  * @param filterInvocation 请求信息         
  * @param attributes       安全配置属性,这里指的是角色         
  * @return 1:同意、-1:反对,返回1时表示有访问权限,-1表示没有访问权限         
  */        
  @Override        
  public int vote(Authentication authentication, FilterInvocation filterInvocation, Collection<ConfigAttribute> attributes) {                
    assert authentication != null;                
    assert filterInvocation != null;                         

    // 没有URL, 拒绝访问                
    String requestURL = getRequestURL(attributes);                
    if (null == requestURL) {                        
      return AccessDecisionVoter.ACCESS_DENIED;                
    }                         

    // 匿名用户, 拒绝访问                
    String userName = authentication.getPrincipal().toString();                
    if (userName.equals("anonymousUser")) {                        
      return AccessDecisionVoter.ACCESS_DENIED;                
    }                          

    // 获取用户信息                
    SystemUser systemUser = systemUserService.queryUserByName(userName);                         

    // 任何人不能删除自己                
    if (this.isDeletingSelf(requestURL, systemUser)) {                        
      return AccessDecisionVoter.ACCESS_DENIED;                
    }                         

    // 依据不同的权限判断是否需要同意操作                
    if (analysisUserRoleUtil.isSuperAdmin(systemUser)) {                        
      return this.superAdminPrivilegeCheck(requestURL, systemUser);                
    }                
    if (analysisUserRoleUtil.isInSuperAdminGroup(systemUser)) {                        
      return this.superAdminGroupMemberPrivilegeCheck(requestURL, systemUser);                
    }                
    if (analysisUserRoleUtil.isGroupAdmin(systemUser)) {                        
      return this.groupAdminPrivilegeCheck(requestURL, systemUser);                
    }                    
      return this.regularUserPrivilegeCheck(requestURL, systemUser);        
  }

该应用是基于角色赋予不同的权限,在后续进行权限判定的过程中,包括但不限于以下两种解决方案:

  1. 将所有需要判定的 URI 放入数据库,检查权限时取出;

  2. 设计文档中规定涉及到的操作和 URI 模版,相互间独立不干涉;鉴权时用正则表达式进行判断;后续添加的新操作需要依据改模版构建 URI。

第一种策略应用模块众多,后续需要新增大量人员,将需要判定的 URI 放入数据库并用表进行连接,可能导致占用较多的数据库存储空间,并不合适。因此我们采用第二种策略进行操作 URI 的判定,缺点是编码量比较大,优点是不会占用太多的额外存储空间。

在构建自定义鉴权投票器的过程中,可能会发现一些需要直接放行的操作,涉及到这部分操作的 URI 我们将其放在配置文件中,并在构建过滤链的时候进行注入,达到绕开投票的目的。

三、总结

以上便是一个根据 SpringBoot DecisionVoter 自定义动态鉴权的例子,具体鉴权逻辑和各种细节需要根据不同的需求进行不同定制化操作,同时需要注意在进行定制化操作时,要保证鉴权过程的高效性和安全性,避免可能存在的安全漏洞和性能问题。此外,还需要考虑系统的可扩展性和可维护性,以便在未来的需求变更或升级过程中能够方便地进行扩展和维护。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/799693.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何使用Git上传代码到GitHub简单教程

学习笔记 1&#xff1a;Github创建自己的一个repositories 2&#xff1a;随便给仓库取个名字 3&#xff1a;接下来&#xff0c;打开git 4&#xff1a;通过 cd 你的本地文件路径 &#xff0c;进入到该路径下 5&#xff1a;初始化项目&#xff0c;在目录下面运行完该代码后会有…

Linux6.16 Docker consul的容器服务更新与发现

文章目录 计算机系统5G云计算第四章 LINUX Docker consul的容器服务更新与发现一、consul 概述1.什么是服务注册与发现2.什么是consul 二、consul 部署1.consul服务器2.registrator服务器3.consul-template4.consul 多节点 计算机系统 5G云计算 第四章 LINUX Docker consul的…

高压放大器在超声加工中的应用

超声加工是一种非常重要的现代加工技术&#xff0c;广泛应用于材料加工、精密制造等领域。在该技术中&#xff0c;高压放大器扮演着至关重要的角色&#xff0c;可以为超声加工提供必要的信号放大和控制功能。 超声加工的原理是通过高频振动产生机械能&#xff0c;并将其传递到被…

编程小白的自学笔记十二(python爬虫入门四Selenium的使用实例二)

系列文章目录 编程小白的自学笔记十一&#xff08;python爬虫入门三Selenium的使用实例详解&#xff09; 编程小白的自学笔记十&#xff08;python爬虫入门二实例代码详解&#xff09; 编程小白的自学笔记九&#xff08;python爬虫入门代码详解&#xff09; 目录 系列文章…

Jmeter性能测试配置

Jmeter检查点/断言 在上一章节中&#xff0c;我们通过调试脚本&#xff0c;通过人工验证脚本可以完成业务功能&#xff0c; 但在性能测试中&#xff0c;我们希望能通过自动验证脚本是否完成业务功能&#xff0c;在Jmeter 中我们可以通过检查点&#xff0c;也就是断言来实现自动…

19.2:纸牌问题

给定一个整型数组arr&#xff0c;代表数值不同的纸牌排成一条线 玩家A和玩家B依次拿走每张纸牌 规定玩家A先拿&#xff0c;玩家B后拿 但是每个玩家每次只能拿走最左或最右的纸牌 玩家A和玩家B都绝顶聪明 请返回最后获胜者的分数 方法一&#xff1a;暴力解法 自然智慧。 pack…

【NLP入门教程】二十三、循环神经网络(RNN)| 小白专场

本教程旨在为初学者提供一份全面而系统的NLP入门指南&#xff0c;探索NLP的核心概念、方法和技术。无论你是计算机科学的新手&#xff0c;还是对自然语言处理领域感兴趣的研究人员&#xff0c;本教程都将为你提供所需的基础知识和实用技能。 专栏地址&#xff1a;&#x1f4d5…

【蓝图】p40-p43对象引用、变量有效性、实现键盘控制物体自转、简单点名系统

p40-p43对象引用、变量有效性、实现键盘控制物体自转、简单点名系统 p40对象引用、变量有效性p41实现键盘控制物体自转创建bool值控制旋转实现通过键盘控制自转 p42p43简单点名系统Get All Actors Of Class&#xff08;获得场景中所有该类的actor演员&#xff09;getFor Each L…

TEE GP(Global Platform)安全认证产品

TEE之GP(Global Platform)认证汇总 一、安全认证产品介绍 选择SECURITY和TEE SECURITY&#xff0c;然后SEARCH&#xff0c;可以看到TEE对应的安全认证产品。 二、HUAWEI ITRUSTEE V3.0 ON KIRIN 980 三、SAMSUNG TEEGRIS V4.1 参考&#xff1a; GlobalPlatform Certification…

【linux】暗夜精灵9自动升级BIOS后,无法进入双系统

1、问题描述 暗夜精灵9自动升级BIOS后,无法进入双系统,甚至没有“多重引导菜单” 2、解决方法 1)进入BIOS 惠普暗夜精灵9进入BIOS的按键是F10 2)设置启动项 需要设置三处 功能键延时:5秒 安全启动模式:关闭 UEFI模式下的开机顺序:ubuntu3)设置显卡切换 如果NVIDIA…

TCL(Tool Command Language)学习(三)-----字符串操作

一、比较 &#xff1a; string compare 格式 &#xff1a; string compare ?-nocase? ?-length int? string1 string2 把字符串 string1 和字符串 string2 进行比较 &#xff0c;返回值为 -1、0或 1 &#xff0c;分别对应 string1 小于、等于或大于string2。如果有 -len…

全球百年品牌,最高已改变25次

摘要&#xff1a;今天&#xff0c;我们看到的那些持久的公司并不是真正持续了100年的公司。在100年的时间里,他们改变了25次、5次或4次,如果不改变,他们就无法生存。 近期市场调研中&#xff0c;许战海咨询发现国内市场已经进入无限内卷的竞争阶段。如何在内卷的竞争中保持可持…

Android OpenGL ES Camera2 实现实时抠图效果

前面文章人像抠图 + OpenGL ES 还能这样玩?没想到吧,我们介绍了利用人像抠图算法生成的 mask 图,然后结合 OpenGL 可以产生一些有趣的效果。 抠图技术应用很广泛,比如很多手机的相机自带“人像留色”滤镜:人体区域保留彩色,人体区域之外灰度化。所以人像留色的关键技术在…

Unity下如何实现低延迟的全景RTMP|RTSP流渲染

技术背景 Unity3D可以用于创建各种类型的的应用程序&#xff0c;包括虚拟现实、培训模拟器等。以下是一些可以使用Unity3D全景播放的场景&#xff1a; 虚拟现实体验&#xff1a;全景视频可以用来创建逼真的虚拟环境&#xff0c;使用户能够感受到身临其境的感觉&#xff1b;培…

Spring中的Bean配置

bean配置 基础配置 别名配置 作用范围配置 1、为什么bean会默认为单例&#xff1f; 如果不为单例&#xff0c;则每一次调用bean&#xff0c;Spring的bean容器中&#xff0c;会出现多个该类的对象&#xff0c;这会造成容器的冗余。 2、适合交给容器进行管理的bean 表现层对象…

Ansys Zemax | 确保自由曲面设计的可制造性

本文专门介绍使用单点金刚石车床加工自由曲面的主要可制造性参数&#xff0c;解释了可制造性参数如何与仪器参数相关联&#xff0c;并展示了如何在 OpticStudio 中检查和控制这些可制造性参数。此外&#xff0c;还解释了如何处理其考察区域外的自由曲面的行为。例如&#xff0c…

MYSQL练习四

练习四 某公司的mis数据库中有部⻔表&#xff08;dept&#xff09;和员工表&#xff08;employee&#xff09;表结构及其数据如下&#xff1a; 表结构 dept表结构 employee表结构 表数据 dept表数据 employee表数据 完成以下SQL编写&#xff1a; 编写脚本&#xff0c;创建…

Eureka注册中心 与 OpenFeign调用接口

需求 一个应用通过接口&#xff0c;调用另一个应用的接口。使用OpenFeign来实现接口调用。 说明 通过OpenFeign&#xff08;本文接下来简称Feign&#xff09;调用远程接口&#xff0c;需要Eureka注册中心的支持。 OpenFeign调用接口的逻辑如下&#xff1a; 提供接口的应用…

617. 合并二叉树

题目 题解一&#xff1a;递归 /*** 递归* param root1* param root2* return*/public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {//结束条件if (root1 null) {return root2;} //结束条件if (root2 null) {return root1;}//两节点数值相加TreeNode me…

C++模拟实现stack

1.前言 stack 遵循的原则是先进后出&#xff0c;那到底是用list 还是 vector呢&#xff1f;其实都可以&#xff0c;但是队列queue就不一样了&#xff0c;他甚至不可以支付vector&#xff0c;因为效率太低了。 但是库里面用了一种新的类型&#xff0c;deque&#xff0c;它的实现…