目录
前言
近期在开展基于若依框架的项目时,面临一些特定业务需求,需进行定制化二次开发。在此记录主要改造工作,为有类似需求的朋友提供参考。需强调的是,本模块基于若依项目二次开发,仅作学习用途,不用于盈利。
项目背景
若依作为完善的后台管理框架,在实际项目中常遭遇特殊场景:
- 多服务架构:主系统采用若依框架,同时存在其他独立部署的下游服务,需统一鉴权。例如,公司有一个核心业务系统基于若依搭建,还有一些辅助的数据分析、文件处理等下游服务,这些服务都要使用主系统的用户鉴权体系。
- Token管理需求:下游服务采用双Token机制(Access Token + Refresh Token),以提升用户体验。
- 游客访问:部分场景下,需允许未登录用户进行有限访问。就像一些电商网站,未登录用户也能浏览商品列表,但不能进行下单等操作。
- 权限精细控制:需对内部服务调用进行特殊处理。
若直接在框架中开发项目固然理想,但由于微服务架构,若强行如此,就必须将鉴权代码复制多份分发到各个微服务中。基于这些需求,开展了一系列改造工作。
主要改造内容
一、远程鉴权模块
1.1 需求场景
项目中存在独立部署的服务,而用户体系需与主系统打通。若每个服务各自维护一套用户权限,管理成本将大幅增加。因此,需实现一种机制,让下游服务能通过网络调用主系统的鉴权接口验证用户权限,如同调用本地服务般便捷,且不改变原先鉴权注解的使用方式。
1.2 实现方案
整体架构如下:
有注解
无注解
通过
失败
下游服务请求
RemoteAuthFilter拦截
检查RemotePreAuthorize注解
提取Token
直接放行
Feign远程调用主系统
主系统验证权限
权限验证结果
继续执行业务逻辑
返回403拒绝访问
1.3 核心组件
新增ruoyi - feign模块,包含以下内容:
| 组件 | 说明 |
|---|---|
@RemotePreAuthorize |
远程鉴权注解,用法与@PreAuthorize一致 |
RemoteAuthFilter |
Servlet版鉴权过滤器 |
RemoteAuthWebFilter |
WebFlux版鉴权过滤器 |
RemoteAuthFeignService |
Feign客户端,调用主系统鉴权接口 |
UserFeignService |
用户相关的Feign客户端(暂未使用) |
1.4 使用方式
在下游服务中,使用方式如下:
java
// 此代码展示了在下游服务的控制器中如何使用远程鉴权注解
// 检查当前用户是否具有system:user:list权限
@RestController
@RequestMapping("/api/data")
public class DataController {
@RemotePreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/users")
public List<User> getUsers() {
// 业务逻辑,获取用户列表
return userService.getUsers();
}
}
下游服务的配置也较为简单:
- 启动类增加@EnableFeignClients并且扫描路径包括com.ruoyi.feign.service
- 有一个最基本的SpringSecurityConfig配置类来覆盖若依的Security配置,否则可能会被拦截。
java
// 该配置类用于启动下游服务,并启用Feign客户端
@SpringBootApplication
@EnableFeignClients(basePackages = "com.ruoyi.feign.service")
public class DownstreamApplication {
public static void main(String[] args) {
SpringApplication.run(DownstreamApplication.class, args);
}
}
1.5 支持的权限表达式
和原先的使用方式保持一致:
| 表达式 | 说明 |
|---|---|
@ss.hasPermi('system:user:list') |
检查单个权限 |
@ss.hasRole('admin') |
检查单个角色 |
@ss.hasAnyPermi('system:user:add,system:user:edit') |
检查多个权限(满足其一即可) |
@ss.hasAnyRoles('admin,manager') |
检查多个角色(满足其一即可) |
二、JWT双Token认证机制
2.1 需求场景
下游服务的前端(此处称为Contract前端)采用JWT + Refresh Token的双Token机制:
- Access Token:有效期较短(如30分钟),用于日常API调用。就像你去图书馆借书,短期的借阅证可以让你在一段时间内自由借阅书籍,但有效期一过就不能再借了。
- Refresh Token:有效期较长(如7天),用于刷新Access Token。它类似于长期的会员证,在短期借阅证过期时,可以用来获取新的短期借阅证。
这样做有诸多好处:
- 短期Token泄露风险更小。
- 用户不需要频繁重新登录。
- Refresh Token可以在服务端主动撤销。
2.2 Token流转流程
数据库 服务端 前端 数据库 服务端 前端 日常API调用 Token过期 登录请求 生成Access Token 生成Refresh Token 存储Refresh Token 返回双Token API请求(Access Token) 返回数据 刷新Token(Refresh Token) 验证Refresh Token 生成新Token对 更新Refresh Token 返回新Token对
2.3 核心实现
新增了JWT工具类,支持:
| 方法 | 说明 |
|---|---|
generateAccessToken() |
生成Access Token |
generateRefreshToken() |
生成Refresh Token |
parseToken() |
解析Token |
isAccessToken() |
判断是否为Access Token |
isRefreshToken() |
判断是否为Refresh Token |
同时新增了刷新令牌管理相关的Service:
SysRefreshToken:刷新令牌实体类SysRefreshTokenMapper:Mapper接口ISysRefreshTokenService:Service接口
2.4 新增接口
| 接口 | 说明 |
|---|---|
POST /auth/refresh |
刷新Token |
POST /auth/guest |
游客登录 |
GET /auth/getInfo |
获取用户信息(适配Contract前端) |
三、游客登录功能
3.1 需求场景
某些场景下,希望允许未注册用户访问系统,但仅能进行有限操作,例如:
- 查看公开数据。
- 使用基础的分析功能。
- 体验部分产品功能。
这就需要游客登录功能。
3.2 实现方式
游客登录流程:
关闭
开启
前端发起游客登录
游客登录开关是否开启
返回错误提示
生成游客用户信息
分配默认游客角色
生成游客Token
返回游客身份信息
核心实现:
- 游客用户特征 :用户名为
guest_+ 随机字符串。 - 默认角色:GUEST角色。
- 默认权限:只有查看和分析权限。
- Token有效期:与普通用户一致。
3.3 使用方式
前端调用:
javascript
// 游客登录的前端请求示例
// 向/auth/guest发送POST请求进行游客登录
POST /auth/guest
// 响应示例,返回游客相关信息
{
"code": 200,
"msg": "操作成功",
"data": {
"user": {
"id": "guest_123456",
"username": "guest_123456",
"role": "GUEST"
},
"accessToken": "xxx",
"refreshToken": "yyy",
"expiresIn": 1800
}
}
系统配置:
sql
-- 在sys_config表中添加配置,用于控制游客登录功能的开关
INSERT INTO sys_config VALUES
(100, '用户管理-游客账户', 'sys.account.guestLogin', 'false',
'是否开启游客登录功能', 'Y', 'admin', NOW(), '', NULL, '游客登录开关');
四、内部服务免鉴权
4.1 需求场景
某些内部服务之间的调用,如主系统调用下游服务,无需经过完整的用户权限验证。此时需要一种机制来识别并豁免这类请求。
4.2 实现方式
新增内部免鉴权配置,和鉴权中心使用同一个密钥进行比对。可能有人担心安全问题,实际上目前考虑到的风险是内部调用注册中心分发这段网络被劫持,不过若这段网络都被劫持,那需要担心的就不只是密钥泄露问题了。要明白攻防是资源的权衡,并非纸上谈兵,不必钻牛角尖。
yaml
# 内部服务免鉴权配置,配置密钥
ruoyi:
remote-auth:
secret: SaltyFishSecretValue
通过特定的请求头或IP白名单来识别内部服务调用,跳过权限验证。
技术要点总结
1. 模块化设计
将远程鉴权功能独立成ruoyi - feign模块,带来诸多优势:
- 代码结构清晰,各模块职责单一。
- 其他服务引入使用方便。
- 便于维护和升级。
2. 兼容性考虑
所有改造都确保与原若依框架兼容:
- 原有的登录、鉴权方式不受影响。
- 新功能通过开关控制,可灵活开启/关闭。
- 下游服务和主系统可独立升级。
3. 安全性考虑
- 采用Token过期机制。
- 将Refresh Token存储在数据库,支持撤销。
- 设置IP白名单限制。
- 内部服务调用使用特定Token。
4. 用户体验
- 双Token机制减少登录次数。
- 游客功能降低使用门槛。
- 统一的鉴权方式,提供一致的使用体验。
使用建议
1. 生产环境配置
- 修改JWT密钥为强密钥。
- 使用HTTPS传输Token。
- 合理设置Token有效期。
2. 权限管理
- 定期审查接口权限配置。
- 严格限制游客权限。
- 妥善保管内部服务Token。
3. 监控告警
- 监控Token刷新频率。
- 记录鉴权失败日志。
- 设置异常访问告警。
写在最后
以上即为主要改造工作,整体未改变若依框架的核心设计理念,仅在特定场景下进行扩展与增强。若大家有类似需求,可参考这些改造方案,欢迎交流讨论。
下期老样子还是聊聊开发过程中遇到的一些问题。
标签:若依框架、二次开发、远程鉴权、JWT双Token、游客登录、内部服务免鉴权