当前位置: 首页 > news >正文

Spring Security的@PreAuthorize注解为什么会知道用户角色?

✅ 核心原理:@PreAuthorize 通过 SecurityContext 获取当前用户信息

@PreAuthorize 本身只是一个“声明式权限注解”,它并不直接知道用户是谁、有什么角色。它的能力来自于 Spring Security 在运行时提供的上下文信息(Security Context)

这个上下文里保存了当前登录用户的详细信息,包括:

  • 用户名
  • 密码(通常不暴露)
  • 权限(Granted Authorities) ← 这就是角色的来源
  • 是否已认证

🔁 完整流程解析

1️⃣ 用户发起请求(如:curl -u admin:123456 ...

HTTP Basic 认证将用户名密码放在请求头中:

Authorization: Basic YWRtaW46MTIzNDU2

Spring Security 拦截该请求,提取用户名 admin 和密码 123456


2️⃣ Spring Security 调用 UserDetailsService.loadUserByUsername()

这是最关键的一环!

你定义了:

@Service
public class CustomUserDetailsService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) {// 从数据库或缓存中查用户// 构建 UserDetails 对象,包含用户名、密码、权限(角色)return new User(username, password, authorities);}
}

其中 authorities 是一个 Collection<? extends GrantedAuthority>,比如:

List<GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_ADMIN"));

👉 重点来了
这里的 "ROLE_ADMIN" 就是角色信息,它被封装进了 UserDetails,最终放入 SecurityContext


3️⃣ 认证成功后,用户信息存入 SecurityContext

Spring Security 会创建一个 Authentication 对象,包含:

  • principal: 用户详情(就是你返回的 UserDetails 实例)
  • credentials: 密码(认证后通常设为 null)
  • authorities: 权限列表
  • authenticated: true

然后把这个 Authentication 放入 SecurityContext

SecurityContextHolder.getContext().setAuthentication(authentication);

从此,整个应用都可以通过 SecurityContextHolder 获取当前用户信息。


4️⃣ 当请求到达 @PreAuthorize 方法时,Spring AOP 拦截并执行权限检查

例如:

@PreAuthorize("hasRole('ROLE_ADMIN')")
public String deleteUser() { ... }

Spring 会:

  1. 从 SecurityContext 中取出 Authentication
  2. 获取 Authentication.getAuthorities()
  3. 检查这些权限中是否包含 ROLE_ADMIN
// 伪代码
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();boolean hasRole = authorities.stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));

如果 hasRole == true,放行;否则抛出 AccessDeniedException


🧠 hasRole() 和 hasAuthority() 的区别

方法说明
hasRole('ADMIN')等价于 hasAuthority('ROLE_ADMIN'),自动加 ROLE_ 前缀
hasAuthority('ROLE_ADMIN')精确匹配权限字符串

所以你在 GrantedAuthority 中存的是 "ROLE_ADMIN",就应该用 hasRole('ADMIN')hasAuthority('ROLE_ADMIN')


📌 总结:@PreAuthorize 是如何知道角色的?

步骤说明
1️⃣用户登录,Spring Security 提取用户名密码
2️⃣调用你实现的 CustomUserDetailsService.loadUserByUsername()
3️⃣你从数据库/缓存中查出用户,并构造 UserDetails,其中包含 GrantedAuthority(如 ROLE_ADMIN
4️⃣认证成功,Authentication 对象存入 SecurityContext
5️⃣请求到达 @PreAuthorize 方法,Spring 从 SecurityContext 取出 Authentication
6️⃣检查 authorities 列表是否包含所需角色
决定是否放行

💡 举个例子:

private UserDetails buildUserDetails(String username, String password, String role) {Collection<? extends GrantedAuthority> authorities =Collections.singletonList(new SimpleGrantedAuthority(role)); // 👈 role = "ROLE_ADMIN"return new org.springframework.security.core.userdetails.User(username, password, authorities);
}

这里你把数据库查到的 role 字段(值为 "ROLE_ADMIN")封装成了一个 GrantedAuthority,它就会被 Spring Security 识别为用户的角色。


🛠️ 验证:你可以在代码中打印当前用户信息

@PreAuthorize("hasRole('ROLE_ADMIN')")
@GetMapping("/debug")
public String debug() {Authentication auth = SecurityContextHolder.getContext().getAuthentication();System.out.println("当前用户: " + auth.getName());System.out.println("用户权限: " + auth.getAuthorities());System.out.println("是否认证: " + auth.isAuthenticated());return "Check console";
}

输出:

当前用户: admin
用户权限: [ROLE_ADMIN]
是否认证: true

结论
@PreAuthorize 能知道用户角色,是因为你通过 UserDetailsService 把角色信息加载进了 UserDetails,而 Spring Security 把它放进了全局上下文 SecurityContext@PreAuthorize 只是读取了这个上下文而已。

如果你不把角色放进 authorities@PreAuthorize 就“不知道”这个角色。

http://www.xdnf.cn/news/1436905.html

相关文章:

  • 开悟篇Docker从零到实战一篇文章搞定
  • 基于Python毕业设计推荐:基于Django的全国降水分析可视化系统
  • 战略咨询——解读81页中小企业企业战略规划方案【附全文阅读】
  • go-mapus最简单的离线瓦片地图协作
  • C++后端开发重点知识点
  • Adafruit_nRF52_Bootloader 使用 uf2
  • Spring Cloud Config 核心原理
  • 【C++】编写通用模板代码的重要技巧:T()
  • CICD的持续集成与持续交付和Zabbix
  • 【C++】15. ⼆叉搜索树
  • 室内定位---apriltag 视觉定位demo
  • (四)Python控制结构(条件结构)
  • deepseek7b本地部署技巧,新手也能玩得转
  • 下载 | Win11 官方精简版,系统占用空间极少!(8月更新、Win 11 IoT物联网 LTSC版、适合老电脑安装使用)
  • Flink RuntimeContext和FunctionContext:状态计算的核心桥梁
  • Linux中断实验
  • 数字化转型的终极关怀:以人为本
  • Linux笔记14——shell编程基础-8
  • C#类对象映射AutoMapper
  • QT(2)
  • MTK Linux DRM分析(二十九)- MTK mtk_dsi.c(Part.1)
  • Linux 环境配置 muduo 网络库详细步骤
  • Linux 文本处理三大利器:命令小工具和sed
  • 从理念到实践:三层解耦架构与“无系统”论
  • 基于web的高校学籍管理系统的设计与实现-(源码+LW+可部署)
  • CodeBuddy 在进化:我只输入了一个地址,完成了OneCode3.0基础开发环境的配置构建
  • JWT在线解密/JWT在线解码 - 加菲工具
  • kukekey在线搭建k8sV1.30.4版本
  • 从栈中取出K个硬币的最大面值和-分组背包
  • 【学Python自动化】 8. Python 错误和异常学习笔记