如何实现高效的动态鉴权
一、概述
Spring Security 是 Spring 框架内高度可定制化的安全框架,也是 Spring 应用的标准安全框架,提供了包括认证和鉴权在内的两大部分。其高度集成于 Spring 框架,无需引入第三方扩展模块,可以避 免大量的数据接口适配问题,大幅度减少开发成本和时间。
如下图所示,Spring Security 的认证鉴权过程实际上位于请求过滤器和拦截器中,在请求通过了所有的过滤器和拦截器之后才会进行 API 适配。换言之,定制 Spring Security 就是修改过滤链中的各种过滤器和拦截器。
认证过程是图中绿色的部分,Spring Security 提供了非常多的认证方式,如密码认证、预认证等;橙色的部分是动态鉴权部分,其内置的 Security Interceptor 会将请求委托给各个具体的 AccessDecisionManager 进行鉴权。
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); }
该应用是基于角色赋予不同的权限,在后续进行权限判定的过程中,包括但不限于以下两种解决方案:
-
将所有需要判定的 URI 放入数据库,检查权限时取出;
-
设计文档中规定涉及到的操作和 URI 模版,相互间独立不干涉;鉴权时用正则表达式进行判断;后续添加的新操作需要依据改模版构建 URI。
第一种策略应用模块众多,后续需要新增大量人员,将需要判定的 URI 放入数据库并用表进行连接,可能导致占用较多的数据库存储空间,并不合适。因此我们采用第二种策略进行操作 URI 的判定,缺点是编码量比较大,优点是不会占用太多的额外存储空间。
在构建自定义鉴权投票器的过程中,可能会发现一些需要直接放行的操作,涉及到这部分操作的 URI 我们将其放在配置文件中,并在构建过滤链的时候进行注入,达到绕开投票的目的。
三、总结
以上便是一个根据 SpringBoot DecisionVoter 自定义动态鉴权的例子,具体鉴权逻辑和各种细节需要根据不同的需求进行不同定制化操作,同时需要注意在进行定制化操作时,要保证鉴权过程的高效性和安全性,避免可能存在的安全漏洞和性能问题。此外,还需要考虑系统的可扩展性和可维护性,以便在未来的需求变更或升级过程中能够方便地进行扩展和维护。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
本地 IDC 中的 K8s 集群如何以 Serverless 方式使用云上计算资源
在前一篇文章《应对突发流量,如何快速为自建 K8s 添加云上弹性能力》中,我们介绍了如何为 IDC 中 K8s 集群添加云上节点,应对业务流量的增长,通过多级弹性调度,灵活使用云上资源,并通过自动弹性伸缩,提高使用率,降低云上成本。 这种直接添加节点的方式,适合需要自定义配置节点(runtime,kubelet,NVIDIA 等),需要特定 ECS 实例规格等场景。同时,这种方式意味您需要自行维护云上节点池。 如果您不想维护云上节点池,您可以选择 Serverless 方式使用阿里云 ECI 弹性容器实例运行业务 Pod,更加高效弹性的使用云上 CPU/GPU 资源。 概述 通过 Serverless 方式使用云上计 CPU/GPU 资源,针对的问题依然是 IDC 中 K8s 集群的弹性能力不足,不能满足业务的快速增长、周期性业务增长和突发业务流量。 通过 Serverless 方式,可以在 K8s 集群直接提交业务 Pod,Pod 将使用阿里云 ECI 弹性容器实例运行,ECI 弹性容器实例启动速度快,与业务 Pod 的生命周期一致,按 Pod 运行时间计费。从而不需要为 IDC 中...
- 下一篇
MySQL5.7 与 MariaDB10.1 审计插件兼容性验证
这是一篇关于发现 MariaDB 审计插件导致 MySQL 发生 crash 后,展开适配验证并进行故障处理的文章。 作者:官永强 爱可生DBA 团队成员,擅长 MySQL 运维方面的技能。热爱学习新知识,亦是个爱打游戏的宅男。 本文来源:原创投稿 爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。 背景 在使用 CentOS Linux release 7.5.1804 (Core) 虚机为 MySQL5.7.34 安装 MariaDB 审计插件时发现:当使用通过解压 mariadb-10.1.48-linux-glibc_ 214-x86_64.tar.gz 获得的 server_audit.so 时,MySQL 会出现 Crash 的情况,通过手动重启 MySQL 也会马上发生 Crash。由此不禁思考: 其他版本的审计插件对该版本MySQL是否也有兼容性问题? 其他版本的MySQL是否也无法使用该版本的审计插件? 对于这样的情况是否有合适的解决方法? 通过查阅官网信息获得 MySQL 5.7 与 MariaDB 10.1 版本审计插件是适配的,于是这里...
相关文章
文章评论
共有0条评论来说两句吧...