RuoYi-Cloud 认证实现

news/2025/10/25 22:58:23/文章来源:https://www.cnblogs.com/xiaochenNN/p/19166103

RuoYi-Cloud 微服务安全认证体系深度解析

1. 整体架构概览

RuoYi-Cloud 的安全认证体系由以下几个核心组件构成:

  1. 网关服务(ruoyi-gateway):统一认证入口
  2. 认证服务(ruoyi-auth):处理用户登录认证
  3. 公共安全模块(ruoyi-common-security):提供安全认证核心逻辑
  4. Redis 缓存:存储用户登录信息

2. 安全认证技术栈

项目采用 JWT + Redis 的无状态认证机制,使用 JWT 进行令牌生成和验证,通过 Redis 缓存用户登录信息,实现分布式环境下的安全认证。

权限控制方式:项目使用自定义注解(@RequiresLogin@RequiresRoles@RequiresPermissions)结合 AOP 切面(PreAuthorizeAspect)实现方法级别的权限控制。

3. 请求处理完整流程

3.1 请求进入网关

当一个请求进入系统时,首先会经过网关服务。网关中配置了 AuthFilter 过滤器进行认证处理:

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();ServerHttpRequest.Builder mutate = request.mutate();String url = request.getURI().getPath();// 跳过不需要验证的路径if (StringUtils.matches(url, ignoreWhite.getWhites())) {return chain.filter(exchange);}// ... 其他认证逻辑
}

3.2 白名单检查

网关首先检查请求路径是否在白名单中。白名单配置在 IgnoreWhiteProperties中:

@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "security.ignore")
public class IgnoreWhiteProperties {private List<String> whites = new ArrayList<>();// ...
}

对于 /login/logout 等路径,网关会直接放行,不进行认证检查。

3.3 JWT 令牌验证

对于不在白名单中的请求,网关会进行 JWT 令牌验证:

String token = getToken(request);
if (StringUtils.isEmpty(token)) {return unauthorizedResponse(exchange, "令牌不能为空");
}
Claims claims = JwtUtils.parseToken(token);
if (claims == null) {return unauthorizedResponse(exchange, "令牌已过期或验证不正确!");
}

3.4 Redis 登录状态验证

验证 JWT 令牌有效后,网关会进一步检查 Redis 中是否存在对应的登录信息:

String userkey = JwtUtils.getUserKey(claims);
boolean islogin = redisService.hasKey(getTokenKey(userkey));
if (!islogin) {return unauthorizedResponse(exchange, "登录状态已过期");
}

3.5 用户信息提取与传递

认证通过后,网关从 JWT 令牌中提取用户信息,并将其设置到请求头中传递给下游服务:

String userid = JwtUtils.getUserId(claims);
String username = JwtUtils.getUserName(claims);
if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) {return unauthorizedResponse(exchange, "令牌验证失败");
}// 设置用户信息到请求
addHeader(mutate, SecurityConstants.USER_KEY, userkey);
addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid);
addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username);

3.6 请求转发到业务服务

网关完成认证后,将请求转发到对应的业务服务。

3.7 业务服务拦截器处理

业务服务中配置了 HeaderInterceptor拦截器,用于处理从网关传递过来的用户信息:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (!(handler instanceof HandlerMethod)) {return true;}SecurityContextHolder.setUserId(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USER_ID));SecurityContextHolder.setUserName(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USERNAME));SecurityContextHolder.setUserKey(ServletUtils.getHeader(request, SecurityConstants.USER_KEY));String token = SecurityUtils.getToken();if (StringUtils.isNotEmpty(token)) {LoginUser loginUser = AuthUtil.getLoginUser(token);if (StringUtils.isNotNull(loginUser)) {AuthUtil.verifyLoginUserExpire(loginUser);SecurityContextHolder.set(SecurityConstants.LOGIN_USER, loginUser);}}return true;
}

3.8 安全上下文设置

HeaderInterceptor 将用户信息存储在 SecurityContextHolder中,这是一个基于TransmittableThreadLocal的线程上下文:

private static final TransmittableThreadLocal<Map<String, Object>> THREAD_LOCAL = new TransmittableThreadLocal<>();

3.9 业务方法权限验证

在业务方法执行前,如果方法上有权限注解(如 @RequiresLogin@RequiresRoles@RequiresPermissions) 、PreAuthorizeAspect 切面会进行权限验证:

@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {// 注解鉴权MethodSignature signature = (MethodSignature) joinPoint.getSignature();checkMethodAnnotation(signature.getMethod());// 执行原有逻辑return joinPoint.proceed();
}

3.10 权限验证逻辑

权限验证通过 AuthUtil 和 AuthLogic 实现:

public void checkMethodAnnotation(Method method) {// 校验 @RequiresLogin 注解RequiresLogin requiresLogin = method.getAnnotation(RequiresLogin.class);if (requiresLogin != null) {AuthUtil.checkLogin();}// 校验 @RequiresRoles 注解RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class);if (requiresRoles != null) {AuthUtil.checkRole(requiresRoles);}// 校验 @RequiresPermissions 注解RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);if (requiresPermissions != null) {AuthUtil.checkPermi(requiresPermissions);}
}

3.11 业务逻辑执行

通过所有认证和权限检查后,业务方法开始执行。在业务逻辑中,可以通过 SecurityContextHolder获取当前用户信息:

String username = SecurityContextHolder.getUserName();
Long userId = SecurityContextHolder.getUserId();

3.12 请求完成清理

请求处理完成后,HeaderInterceptor 会清理线程上下文:

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {SecurityContextHolder.remove();
}

4. 典型请求流程示例

4.1 登录请求流程

  1. 用户发起 /login 请求
  2. 请求到达网关,由于 /login 在白名单中,直接放行
  3. 请求转发到认证服务 ruoyi-auth
  4. TokenController 处理登录请求
  5. SysLoginService 验证用户名密码
  6. 验证通过后,TokenService 生成 JWT 令牌并存储用户信息到 Redis
  7. 返回 JWT 令牌给客户端

4.2 业务请求流程

  1. 用户携带 JWT 令牌发起业务请求(如 /user/info
  2. 请求到达网关
  3. 网关检查路径不在白名单中,进行认证处理
  4. 网关验证 JWT 令牌有效性
  5. 网关检查 Redis 中是否存在对应的登录信息
  6. 认证通过后,将用户信息添加到请求头中
  7. 请求转发到用户服务
  8. 用户服务的 HeaderInterceptor 处理请求头中的用户信息
  9. 如果业务方法上有权限注解,PreAuthorizeAspect 进行权限验证
  10. 业务方法执行,通过 SecurityContextHolder 获取用户信息
  11. 返回业务结果给客户端

5. 安全特性

5.1 无状态认证

使用 JWT 实现无状态认证,服务端不需要存储会话信息,便于水平扩展。

5.2 Redis 缓存增强安全性

虽然 JWT 本身是无状态的,但通过 Redis 存储用户登录信息,可以实现登录状态的管理和强制下线等功能。

5.3 双重验证机制

同时验证 JWT 令牌的有效性和 Redis 中的登录状态,提高安全性。

5.4 细粒度权限控制

支持基于角色和权限的细粒度访问控制,通过注解方式实现方法级别的权限验证。

5.5 线程上下文传递

通过 TransmittableThreadLocal 实现用户信息在线程间的传递,保证在异步环境下也能正确获取用户信息。

结论

RuoYi-Cloud 的安全认证体系通过网关统一认证和微服务内部权限验证的方式,构建了一个完整、安全、可扩展的认证解决方案。整个流程从请求进入网关开始,经过令牌验证、权限检查,到最后的业务处理和上下文清理,形成了一个闭环的安全保障体系。这种设计既保证了系统的安全性,又具有良好的可维护性和扩展性,为微服务架构下的安全认证提供了优秀的实践范例。

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

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

相关文章

CobaltStrike流量分析

CobaltStrike流量分析 1.溯源反制,提交黑客CS服务器的flag.txt内容 使用nmap扫描IP开放端口,发现开放了一个 2357端口 我们看看里面有什么可以看到对方IP开放了一个2375端口,我们看看有没有什么利用的方法 一、端口…

2025年自动上料机厂家权威推荐榜:螺旋上料机/真空上料机/粉末上料机,高效输送系统精准选型指南

2025年自动上料机厂家权威推荐榜:螺旋上料机/真空上料机/粉末上料机,高效输送系统精准选型指南 在工业自动化快速发展的今天,自动上料机作为生产线的重要环节,其性能直接影响生产效率和产品质量。螺旋上料机、真空…

建立VLAN间通信

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

用代码将txt分别转换成列表和字典

txt = """男孩:boy 女孩:girl 姓名:name 年龄:age 性别:sex""" # 转换为字符串列表(每行一个元素) str_list = [line for line in txt.split(\n)] # 创建中-英字典 zh_en_dict =…

AtCoder Beginner Contest 429 ABCDEF 题目解析

A - Too Many Requests 题意 给定正整数 \(N\) 和 \(M\)。 输出 \(N\) 行,对于第 \(i\) 行:如果 \(i\leq M\) ,则输出 OK 否则输出 Too Many Requests代码 void solve() {int n, m;cin >> n >> m;for(i…

2025年提升机厂家推荐排行榜,自动提升机,垂直提升机,物料提升机,工业提升设备公司精选

2025年提升机厂家推荐排行榜:自动提升机、垂直提升机、物料提升机、工业提升设备公司精选 在工业自动化浪潮持续深入的今天,提升设备作为物料输送系统的核心组成部分,正经历着技术革新与产业升级的双重变革。自动提…

刷题日记—数组—布尔数组的应用

前几天刷题碰到了种树,切方块类型的题目,这类题目用布尔类型判断每一个个体的状态,最后根据每个元素对应的布尔值来统计数目:如下: 1.移数问题:解题步骤如下:```plaintext include using namespace std; bool fl…

How to Build an Agent

How to Build an Agent https://www.bilibili.com/video/BV1G2uSzqErU/?spm_id_from=333.788.videopod.sections&vd_source=57e261300f39bf692de396b55bf8c41b https://blog.langchain.com/how-to-build-an-agent…

树状数组 区间加 区间和 小记

树状数组 区间加 & 区间和 小记 考虑差分数组的变化,即 \(d_i=a_i-a_{i-1}\)。 那么区间加时,会使 \(d_l\gets d_l+val,d_{r+1}\gets d_{r+1}-val\)。 考虑求区间和,转化为求前缀的和,即求 \[\begin{aligned} …

if 语句

代码缩进为一个 tab 键,或者四个空格,建议用四个空格。 同级代码必须缩进相同空格,if 和 else 里面缩进不同没关系,比如 if 下面缩进是 4 个空格,else 下面缩进是 2 个空格,是完全没有问题的。 程序示例: weath…

深入解析:ue编辑器视口鼠标消失的问题

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

详细介绍:k8s中的kubelet

详细介绍:k8s中的kubeletpre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", &…

详细介绍:React Native 中的 useState、Context

详细介绍:React Native 中的 useState、Contextpre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", …

【MySQL】Oracle与MySQL,跨库数据转储 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

昨夜雨疏风骤

热爱过分浅薄 习惯劳而不获好像把一切都搞得很糟糕。 每天都在胡思乱想胆战心惊。 我什么时候这么拘泥了? I literally want.... 心虚。空虚。 差不多得了。 把别人想的太复杂。是自己邪恶的表现。 你是一个需要专心…

明天的任务

1、找到目标即所有能想到的让自己念头不通达的问题,然后总结这些领域的”基本现实“ 2、制定相应的解决计划 3、行动,并在行动中不断修正和完善 基本现时 和计划,但是目标不能动,即不能半途而废。 一、 1、如何通过…

Windows SMB权限提升漏洞遭活跃利用

CISA警告Windows SMB权限提升漏洞正被活跃利用,微软发布带外修复补丁。俄罗斯COLDRiver组织快速更新恶意软件库,GlassWorm恶意软件使用隐形Unicode字符隐藏恶意逻辑。欧洲当局捣毁拉脱维亚大型SIM农场运营。安全漏洞…

深度神经网络 —— 使用深度自动编码器进行手写数字的去噪音

深度神经网络 —— 使用深度自动编码器进行手写数字的去噪音代码:训练后的本博客是博主个人学习时的一些记录,不保证是为原创,个别文章加入了转载的源地址,还有个别文章是汇总网上多份资料所成,在这之中也必有疏漏…

深度神经网络 —— 使用深度自动编码器进行手写数字的去噪音

深度神经网络 —— 使用深度自动编码器进行手写数字的去噪音代码:训练后的本博客是博主个人学习时的一些记录,不保证是为原创,个别文章加入了转载的源地址,还有个别文章是汇总网上多份资料所成,在这之中也必有疏漏…