Spring Security 的方法级权限控制是如何利用 AOP 的?

Spring Security 的方法级权限控制是 AOP 技术在实际应用中一个极其强大的应用典范。它允许我们以声明式的方式保护业务方法,将安全规则与业务逻辑彻底解耦。

核心思想:权限检查的“门卫”

你可以把 AOP 在方法级安全中的作用想象成一个尽职尽责的“门卫”。

  • 目标方法 (deleteUser): 一个重要的、需要保护的房间。
  • 调用方 (Caller): 想要进入这个房间的人。
  • 权限注解 (@PreAuthorize, @Secured): 贴在门上的“入内规则”(例如,“仅限管理员入内”)。
  • AOP 代理 (Proxy): 站在门口的门卫

当有人想进入房间时,他接触到的不是房间本身,而是门卫。门卫会先查看门上的规则,然后检查这个人的身份(权限)。如果符合规则,就放行;如果不符合,就直接把他拦在门外,这个人根本没机会进入房间。


实现原理:AOP 拦截与决策

整个过程是通过一个特殊的 AOP 环绕通知 (@Around)前置通知 (@Before) 来实现的,这个通知就是 Spring Security 提供的 MethodSecurityInterceptor

Step 1: 开启方法级安全

首先,你需要在你的配置类中开启方法级安全的功能。

import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;@EnableGlobalMethodSecurity(prePostEnabled = true, // 启用 @PreAuthorize 和 @PostAuthorizesecuredEnabled = true, // 启用 @Securedjsr250Enabled = true   // 启用 @RolesAllowed
)
public class MethodSecurityConfig {// ...
}

@EnableGlobalMethodSecurity 这个注解告诉 Spring:“请开始扫描带有方法级安全注解的 Bean,并为它们应用 AOP 增强。”

Step 2: AOP 代理创建

@Transactional 一样,当 Spring 容器发现一个 Bean (例如 AdminService) 的方法上带有 @PreAuthorize 等注解时,它不会直接使用原始的 AdminService 实例。相反,它会:

  1. AdminService 创建一个 AOP 代理对象
  2. 这个代理对象中织入了 MethodSecurityInterceptor
  3. 容器中最终存在的是这个被增强了的代理对象。
Step 3: 方法调用与安全拦截 (核心)

当你的代码调用一个被保护的方法时(例如 adminServiceProxy.deleteUser(1L)),流程如下:

  1. 调用被代理拦截
    调用首先命中代理对象

  2. MethodSecurityInterceptor 被激活
    这个安全拦截器开始工作,它的逻辑可以看作一个前置通知(对于 @PreAuthorize)或环绕通知

    a. 【方法执行前】进行权限决策:
    * 拦截器会查找当前被调用方法上的安全注解,例如 @PreAuthorize("hasRole('ADMIN')")
    * 它会从 SecurityContextHolder 中获取当前用户的 Authentication 对象,这个对象包含了用户的身份信息和拥有的权限(如角色、权限字符串等)。
    * 它会使用 SpEL (Spring Expression Language) 解析器来执行注解中的表达式,例如 hasRole('ADMIN')
    * 解析器会拿当前用户的权限和表达式进行比对。

    b. 【做出决策】放行或拒绝:
    * 如果表达式评估为 true (权限匹配)
    拦截器认为检查通过,就会继续执行调用链,最终调用到你原始的 AdminServicedeleteUser 业务逻辑方法。
    * 如果表达式评估为 false (权限不足)
    拦截器会立即中断调用链直接抛出一个 AccessDeniedException (访问被拒绝异常)。你的核心业务方法 deleteUser 根本不会被执行

    c. 异常处理:
    * 这个 AccessDeniedException 会被 Spring Security 的异常处理机制捕获,通常会向客户端返回一个 403 Forbidden 的 HTTP 状态码。

图解实现原理

+----------------+
| Caller         |
+----------------+| 1. 调用 adminService.deleteUser()v
+--------------------------------------------------------------------------+
|              AdminService 代理对象 (Proxy)                               |
|                                                                          |
|   +------------------------------------------------------------------+   |
|   |          MethodSecurityInterceptor (AOP Advice)                  |   |
|   |                                                                  |   |
|   |    2. 【方法执行前】                                               |   |
|   |       - 查找注解: @PreAuthorize("hasRole('ADMIN')")                |   |
|   |       - 获取当前用户权限 (from SecurityContext)                    |   |
|   |       - 评估表达式: hasRole('ADMIN') ?                           |   |
|   |                                                                  |   |
|   |    3a. 【权限匹配】if (true) {                                    |   |
|   |            继续调用 -> target.deleteUser() --> [核心业务逻辑]      |   |
|   |        }                                                         |   |
|   |                                                                  |   |
|   |    3b. 【权限不足】if (false) {                                   |   |
|   |            throw new AccessDeniedException();  <-- 中断流程       |   |
|   |        }                                                         |   |
|   |                                                                  |   |
|   +------------------------------------------------------------------+   |
|                                                                          |
+--------------------------------------------------------------------------+^                                                               || 4. (成功) 返回结果 or (失败) AccessDeniedException             ||                                                               |+---------------------------------------------------------------+

代码示例

@Service
public class DocumentService {// 只有拥有 'ROLE_ADMIN' 角色或 'WRITE_DOCUMENT' 权限的用户才能调用@PreAuthorize("hasRole('ADMIN') or hasAuthority('WRITE_DOCUMENT')")public void editDocument(String docId) {System.out.println("Executing: Editing document " + docId);// ... 核心业务逻辑 ...}// @PostAuthorize: 在方法执行后进行权限检查,可以基于返回值进行判断// 只有当返回的文档所有者是当前用户时,才允许返回@PostAuthorize("returnObject.owner == authentication.name")public Document getDocument(String docId) {System.out.println("Executing: Fetching document " + docId);// ... 从数据库获取文档 ...return findDocumentInDb(docId); }
}

总结

  • 核心技术:Spring AOP 的前置通知环绕通知
  • 关键角色
    • @EnableGlobalMethodSecurity: AOP 功能的“总开关”。
    • 代理对象 (Proxy):拦截方法调用。
    • MethodSecurityInterceptor: 实现了具体的安全检查逻辑。
    • SecurityContextHolder: 提供当前用户的认证和权限信息。
  • 工作流程
    1. 代理拦截:调用被代理对象拦截。
    2. 权限检查:在目标方法执行前,拦截器根据注解和当前用户权限进行决策。
    3. 放行或中断:如果权限足够,则执行业务方法;如果不足,则直接抛出异常,中断执行。

通过 AOP,Spring Security 将复杂的权限校验逻辑从业务代码中优雅地分离出去,使得代码更加清晰、安全规则更易于管理。

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

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

相关文章

一键内网穿透,无需域名和服务器,自动https访问

cloudflare能将内网web转为外网可访问的地址。&#xff08;这和apiSQL有点类似&#xff0c;apiSQ可以将内网数据库轻松转换为外网的API&#xff0c;并且还支持代理内网已有API&#xff0c;增强安全增加API Key&#xff0c;以https访问等等&#xff09; 但Cloudfalre tunnel这个…

Sentinel(二):Sentinel流量控制

一、Sentinel 流控规则基本介绍 1、Snetinel 流控规则配置方式 Sentinel 支持可视化的流控规则配置&#xff0c;使用非常简单&#xff1b;可以在监控服务下的“簇点链路” 或 “流控规则” 中 给指定的请求资源配置流控规则&#xff1b;一般推荐在 “簇点链路” 中配置流控规则…

支持PY普冉系列单片机调试工具PY32linK仿真器

PY32 Link是专为 ‌PY32系列ARM-Cortex内核单片机‌&#xff08;如PY32F002A/030/071/040/403等&#xff09;设计的仿真器&#xff0c;支持全系列芯片的‌调试和仿真‌功能。‌开发环境兼容性‌支持主流IDE&#xff1a;‌Keil MDK‌ 和 ‌IAR Embedded Workbench‌&#xff0c;…

深入解析Python多服务器监控告警系统:从原理到生产部署

深入解析Python多服务器监控告警系统&#xff1a;从原理到生产部署 整体架构图 核心设计思想 无代理监控&#xff1a;通过SSH直接获取数据&#xff0c;无需在目标服务器安装代理故障隔离&#xff1a;单台服务器故障不影响整体监控多级检测&#xff1a;网络层→资源层→服务层层…

JUC:10.线程、monitor管程、锁对象之间在synchronized加锁的流程(未完)

一、monitor管程工作原理&#xff1a; 首先&#xff0c;synchronized是一个对象锁&#xff0c;当线程运行到某个临界区&#xff0c;这个临界区使用synchronized对对象obj进行了上锁&#xff0c;此时底层发生了什么&#xff1f; 1.当synchronized对obj上锁后&#xff0c;synch…

Elasticsearch(ES)分页

Elasticsearch&#xff08;简称 ES&#xff09;本身不适合传统意义上的“深分页”&#xff0c;但提供了多种分页方式&#xff0c;每种适用不同场景。我们来详细讲解&#xff1a; 一、基本分页&#xff08;from size&#xff09; 最常用的分页方式&#xff0c;类似 SQL 的 LIM…

原生微信小程序:用 `setData` 正确修改数组中的对象项状态(附实战技巧)

&#x1f4cc; 背景介绍 在微信小程序开发中&#xff0c;我们经常需要修改数组中某个对象的某个字段&#xff0c;比如&#xff1a; 列表中的某一项展开/收起多选状态切换数据列表中的临时标记等 一个常见的场景是&#xff1a; lists: [{ show: true }, { show: true }, { s…

Oracle 临时表空间相关操作

一、临时表空间概述 临时表空间&#xff08;Temporary Tablespace&#xff09;是Oracle数据库中用于存储临时数据的特殊存储区域&#xff0c;其数据在会话结束或事务提交后自动清除&#xff0c;重启数据库后彻底消失。主要用途包括&#xff1a; 存储排序操作&#xff08;如OR…

从静态到动态:Web渲染模式的演进和突破

渲染模式有好多种&#xff0c;了解下web的各种渲染模式&#xff0c;对技术选型有很大的参考作用。 一、静态HTML时代 早期&#xff08;1990 - 1995年&#xff09;网页开发完全依赖手工编写HTML&#xff08;HyperText Markup Language&#xff09;和CSS&#xff08;层叠样式表…

Flask(六) 数据库操作SQLAlchemy

文章目录 一、准备工作二、最小化可运行示例✅ 补充延迟绑定方式&#xff08;推荐方式&#xff09; 三、数据库基本操作&#xff08;增删改查&#xff09;1. 插入数据&#xff08;增&#xff09;2. 查询数据&#xff08;查&#xff09;3. 更新数据&#xff08;改&#xff09;4.…

PYTHON从入门到实践7-获取用户输入与while循环

# 【1】获取用户输入 # 【2】python数据类型的转换 input_res input("请输入一个数字\n") if int(input_res) % 10 0:print("你输入的数是10的倍数") else:print("你输入的数不是10的倍数") # 【3】while循环&#xff0c;适合不知道循环多少次…

学习笔记(C++篇)—— Day 8

1.STL简介 STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 2.STL的六大组件 先这样&#xff0c;下一部分是string的内容&#xff0c;内容比较多&a…

ant+Jmeter+jenkins接口自动化,如何实现把执行失败的接口信息单独发邮件?

B站讲的最好的自动化测试教程&#xff0c;工具框架附项目实战一套速通&#xff0c;零基础完全轻松掌握&#xff01;自动化测试课程、web/app/接口 实现AntJMeterJenkins接口自动化失败接口邮件通知方案 要实现只发送执行失败的接口信息邮件通知&#xff0c;可以通过以下步骤实…

恶意Python包“psslib“实施拼写错误攻击,可强制关闭Windows系统

Socket威胁研究团队发现一个名为psslib的恶意Python包&#xff0c;该软件包伪装成提供密码安全功能&#xff0c;实则会突然关闭Windows系统。这个由化名umaraq的威胁行为者开发的软件包&#xff0c;是对知名密码哈希工具库passlib的拼写错误仿冒&#xff08;typosquatting&…

云原生灰度方案对比:服务网格灰度(Istio ) 与 K8s Ingress 灰度(Nginx Ingress )

服务网格灰度与 Kubernetes Ingress 灰度是云原生环境下两种主流的灰度发布方案&#xff0c;它们在架构定位、实现方式和适用场景上存在显著差异。以下从多个维度对比分析&#xff0c;并给出选型建议&#xff1a; 一、核心区别对比 维度服务网格灰度&#xff08;以 Istio 为例…

科技如何影响我们的生活?

科技已成为我们生活中不可或缺的一部分&#xff0c;彻底改变了我们工作、沟通和生活的方式。从智能手机到智能家居&#xff0c;科技已渗透到我们生活的每个角落。无论是用手机闹钟开启新的一天&#xff0c;通过 Alexa 开关灯光&#xff0c;还是打开 Uber 或 Lyft 打车上班&…

Re--攻防世界-基础android

Jadx 可以看到有账号密码输入 进入checkPassword函数 分析一下&#xff1a; 对每个字符 pass[len] 进行以下计算 pass[len] (char) (((255 - len) - 100) - pass[len]); 解密脚本 def decrypt_password(): password [] for len in range(12): c (255 - le…

InnoDB表空间结构-系统表空间

系统表空间整体结构 页号为7的SYS类型页结构

如何构建知识库

构建个人知识库是一个系统化的过程&#xff0c;需要结合工具选择、信息管理和持续优化。以下是分步骤的实用指南&#xff0c;包含现代工具和方法的建议&#xff1a; 一、明确知识库定位&#xff08;Why&#xff09; ​核心目标​ 学习型&#xff1a;支持学术研究/职业发展&…

3 大语言模型预训练数据-3.2 数据处理-3.2.2 冗余去除——2.SimHash算法文本去重实战案例:新闻文章去重场景

SimHash算法文本去重实战案例&#xff1a;新闻文章去重场景 一、案例背景与目标二、具体实现步骤与示例1. **待去重文本示例**2. **步骤1&#xff1a;文本预处理与特征提取**3. **步骤2&#xff1a;特征向量化与哈希映射**4. **步骤3&#xff1a;特征向量聚合**5. **步骤4&…