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

RuoYi-Cloud 认证实现

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.hskmm.com/?act=detail&tid=39098

相关文章:

  • 初步学习计算机相关知识有感 - fang
  • 2025年自动上料机厂家权威推荐榜:螺旋上料机/真空上料机/粉末上料机,高效输送系统精准选型指南
  • 用代码将txt分别转换成列表和字典
  • 每日反思(2025_10_25)
  • AtCoder Beginner Contest 429 ABCDEF 题目解析
  • 2025年提升机厂家推荐排行榜,自动提升机,垂直提升机,物料提升机,工业提升设备公司精选
  • 刷题日记—数组—布尔数组的应用
  • 详细介绍:k8s中的kubelet
  • 树状数组 区间加 区间和 小记
  • 实验二 现代C++编程初体验
  • 昨夜雨疏风骤
  • 明天的任务
  • Windows SMB权限提升漏洞遭活跃利用
  • 江西振兴杯决赛Misc全解
  • 完整教程:Webpack5 第四节
  • 2025.10.25总结
  • ABC429
  • 10.25 CSP-S模拟39/2025多校冲刺CSP模拟赛8 改题记录
  • ABC429(C,D,E)
  • 玩转单片机之智能车小露——数字与字符串的转换与打印
  • 数据采集作业1 102302111 海米沙
  • 嵌入子流形
  • Link-Cut Tree
  • 列表,集合,字典的增、删、查、改方法对比
  • MusicFree 音乐
  • 线段上随机取n个点的最大距离期望
  • RuoYi-Cloud-Plus 数据权限实现原理解析
  • 第5天(中等题 滑动窗口、逆向思维)
  • P10老板一句‘搞不定就P0’,15分钟我用Arthas捞回1000万资损 - 指南
  • 华为堡垒机