外观
权限验证拦截器 🔐
功能介绍 💡
TIP
权限验证拦截器就像一个智能门禁系统,负责管理用户的会话(Session)和权限验证。支持Redis和JWT两种验证方式,可以灵活应对不同的应用场景。
核心功能
- ✅ 会话管理(Redis/JWT)
- ✅ 权限验证
- ✅ 动态更新session
详细配置说明 ⚙️
核心参数说明
参数名 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
enabled | boolean | 否 | false | 是否启用拦截器 |
rules | array | 是 | - | 规则列表 |
权限配置详解 (rules)
基础配置
参数名 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
type | string | 是 | - | 存储类型:redis/jwt |
urls | array | 是 | - | 需要拦截的URL列表 |
authorization-name | string | 否 | authorization | 令牌参数名 |
business-key | string | 否 | bearer | 业务标识(多系统区分用) |
expire | Duration | 否 | - | 过期时间(如:30m) |
is-generate-temp-code | boolean | 否 | false | 是否生成临时码 |
Redis专用配置
参数名 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
redis-auto-expire | boolean | 否 | false | 是否自动延期 |
JWT专用配置
参数名 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
jwt-secret | string | 是 | - | JWT密钥 |
is-crypto | boolean | 否 | false | 是否启用加密 |
encrypt-type | string | 否 | DES3 | 加密算法类型,支持DES3、SM4 |
crypto-key | string | 否 | - | 数据加密密钥(DES3算法要求24位,SM4算法要求16位) |
jwt-check-logout | boolean | 否 | false | 是否验证退出状态 |
权限控制配置
参数名 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
security-enabled-admin-authority | boolean | 否 | false | 是否启用管理员权限 |
security-visitor-urls | array | 否 | - | 游客可访问地址 |
security-login-urls | array | 否 | - | 登录后可访问地址 |
security-forbid-urls | array | 否 | - | 禁止访问地址 |
用户相关配置
参数名 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
user-login-urls | array | 否 | - | 登录接口地址 |
user-logout-urls | array | 否 | - | 退出接口地址 |
user-get-info-urls | array | 否 | - | 获取用户信息接口 |
user-get-session-urls | array | 否 | - | 获取会话信息接口 |
user-login-type | string | 否 | - | 用户登录类型,local 本地用户验证 |
accounts | array | 否 | - | 本地用户配置列表 |
session-update-enabled | boolean | 否 | false | 是否启用session更新(仅redis模式生效) |
session-update-interval | Duration | 否 | - | session更新间隔时间,超过此时间自动更新session(如:10m) |
session-update-url | string | 否 | - | session更新的URI地址 |
session-update-timeout | Duration | 否 | - | session更新请求超时时间(如:30s) |
本地用户配置[accounts]
参数名 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
account-id | string | 是 | - | 账户ID |
account-no | string | 是 | - | 账号 |
password | string | 是 | - | 密码 |
nickname | string | 是 | - | 昵称 |
is-admin | boolean | 否 | false | 是否管理员 |
authorize-urls | array | 否 | - | 授权的URL列表 |
authorize-codes | array | 否 | - | 授权的权限码列表 |
配置示例 📋
Redis存储配置
yaml
gateway:
filter:
session:
enabled: true
rules:
- type: redis # 使用Redis存储
urls: # 拦截的URL
- /demo/**
business-key: demo # 业务标识
expire: 1h # 1小时过期
redis-auto-expire: true # 自动续期
user-login-urls: # 登录接口
- /demo/login
user-logout-urls: # 退出接口
- /demo/logout
security-visitor-urls: # 游客权限
- /demo/captcha
- /demo/login
security-login-urls: # 登录权限
- /demo/getInfo
本地用户验证配置
不需要调用微服务接口,直接配置一个用户
yaml
gateway:
filter:
session:
enabled: true
rules:
- type: redis # 使用Redis存储
urls: # 拦截的URL
- /demo/**
business-key: demo # 业务标识
expire: 1h # 1小时过期
redis-auto-expire: true # 自动续期
user-login-urls: # 登录接口
- /demo/login
user-login-type: local
accounts:
- account-no: admin
account-id: 1
password: 1234567
nickname: 管理员11
is-admin: true
authorize-urls:
- /admin/**
authorize-codes:
- adddd
user-logout-urls: # 退出接口
- /demo/logout
security-visitor-urls: # 游客权限
- /demo/captcha
- /demo/login
security-login-urls: # 登录权限
- /demo/getInfo
JWT存储配置
yaml
gateway:
filter:
session:
enabled: true
rules:
- type: jwt
urls:
- /demo/**
jwt-secret: "a748d0d6-1b30-4489-9751-1115134dcb8a"
is-crypto: true
crypto-key: "a748d0d6a748d0d6a748d0d6"
jwt-check-logout: true
登录接口开发 💻
返回对象要求
java
@PostMapping(value = "/login", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<ResultVo<SessionVo>> login(@RequestBody @Valid LoginDto dto) {
return Mono.just(ResultVo.ok(loginService.login(dto)));
}
登录逻辑示例
java
@Override
public SessionVo login(LoginDto dto) {
// 1. 验证登录信息
if (!"admin".equals(dto.getAccountNo()) || !"123456".equals(dto.getPassword())) {
throw new InvokeException(-1, "用户名或密码不正确");
}
// 2. 创建会话信息
SessionVo session = new SessionVo();
session.setIsSystem(true); // 是否管理员
session.setUserId("1");
session.setNickname("wueasy");
// 3. 设置权限信息
Set<String> authorizeUrls = new HashSet<>();
authorizeUrls.add("/api/admin/**");
session.setAuthorizeUrlList(authorizeUrls);
return session;
}
动态更新Session说明 🔄
功能介绍
动态更新Session功能允许在用户会话期间实时更新用户的权限信息,无需重新登录。这在以下场景中特别有用:
- 用户权限发生变更时
- 用户角色调整时
- 数据权限范围变化时
- 需要实时同步最新权限状态时
配置要求
要启用动态更新Session功能,需要在配置中添加以下参数:
yaml
gateway:
filter:
session:
enabled: true
rules:
- type: redis # 仅Redis模式支持动态更新
urls:
- /demo/**
business-key: demo
expire: 1h
redis-auto-expire: true
# 动态更新Session配置
session-update-enabled: true # 启用session更新
session-update-interval: 10m # 更新间隔:10分钟
session-update-url: "http://localhost:8082/getLastSession" # 更新接口地址
session-update-timeout: 30s # 请求超时时间
user-login-urls:
- /demo/login
接口开发
1. Controller层实现
java
/**
* 获取最新的Session信息
* 注意:此接口会被网关自动调用,用于更新用户会话信息
*/
@PostMapping(value = "/getLastSession", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<ResultVo<SessionVo>> getLastSession() {
return Mono.just(ResultVo.ok(loginService.getLastSession()));
}
2. Service层实现
java
@Override
public SessionVo getLastSession() {
// 创建最新的会话信息
SessionVo session = new SessionVo();
session.setIsSystem(false); // 是否超级管理员
session.setUserId("1"); // 用户ID
session.setNickname("wueasy234"); // 用户昵称
// 设置权限代码集合(用于前端权限判断)
Set<String> authorizeCodeList = new HashSet<String>();
authorizeCodeList.add("user");
authorizeCodeList.add("admin"); // 可以动态添加新权限
session.setAuthorizeCodeList(authorizeCodeList);
// 设置可访问的URL地址权限
Set<String> linkUrlSetAll = new HashSet<String>();
linkUrlSetAll.add("/demo/get");
linkUrlSetAll.add("/demo/list"); // 可以动态添加新的URL权限
session.setAuthorizeUrlList(linkUrlSetAll);
// 设置数据权限映射(重要:用于数据权限验证拦截器)
Map<String, Set<String>> dataAuthorizeMap = new HashMap<>();
Set<String> demoDataSet = new HashSet<>();
demoDataSet.add("测试1");
demoDataSet.add("测试2");
demoDataSet.add("测试3"); // 可以动态添加新的数据权限
dataAuthorizeMap.put("demo", demoDataSet);
// 可以添加多个业务的数据权限
Set<String> userDataSet = new HashSet<>();
userDataSet.add("org001");
userDataSet.add("org002");
dataAuthorizeMap.put("USER_DATA", userDataSet);
session.setDataAuthorizeMap(dataAuthorizeMap);
// 设置更新字段列表(指定本次更新哪些字段)
List<String> updateFieldList = new ArrayList<>();
updateFieldList.add("nickname"); // 更新昵称
updateFieldList.add("authorizeCodeList"); // 更新权限代码
updateFieldList.add("authorizeUrlList"); // 更新URL权限
updateFieldList.add("dataAuthorizeMap"); // 更新数据权限
session.setUpdateFieldList(updateFieldList);
return session;
}
工作原理
- 触发条件:当用户访问受保护的URL时,网关会检查Session的更新时间
- 时间判断:如果距离上次更新超过了配置的
session-update-interval
时间 - 自动调用:网关会自动调用配置的
session-update-url
接口 - 更新Session:获取最新的Session信息并更新到Redis中
- 继续处理:使用更新后的Session信息继续处理当前请求
SessionVo对象结构说明
java
public class SessionVo {
private String userId; // 用户ID
private String nickname; // 用户昵称
private Boolean isSystem; // 是否超级管理员
private Set<String> authorizeCodeList; // 权限代码集合
private Set<String> authorizeUrlList; // 可访问URL集合
private Map<String, Set<String>> dataAuthorizeMap; // 数据权限映射
/**
* 更新字段集合,只有更新session时使用
* 用于指定本次更新需要更新哪些字段,未指定的字段将保持原值
*/
private List<String> updateFieldList;
// 其他字段...
}
数据权限映射说明
dataAuthorizeMap
是一个重要的字段,用于数据权限验证拦截器:
java
Map<String, Set<String>> dataAuthorizeMap = new HashMap<>();
// 业务代码 -> 权限值集合
dataAuthorizeMap.put("USER_DATA", Set.of("org001", "org002")); // 用户数据权限
dataAuthorizeMap.put("ORDER_DATA", Set.of("merchant001")); // 订单数据权限
dataAuthorizeMap.put("DEPT_DATA", Set.of("dept001", "dept002")); // 部门数据权限
updateFieldList字段详解
updateFieldList
字段用于精确控制Session更新的范围,提高更新效率并避免不必要的数据覆盖。
支持的字段名称
字段名 | 说明 | 示例 |
---|---|---|
nickname | 用户昵称 | 用户修改昵称时 |
avatarUrl | 头像地址 | 用户修改头像时 |
isSystem | 超级管理员标识 | 用户角色变更时 |
authorizeCodeList | 权限代码集合 | 权限变更时 |
authorizeUrlList | URL权限集合 | URL访问权限变更时 |
dataAuthorizeMap | 数据权限映射 | 数据权限范围变更时 |
extendedMap | 拓展map | 扩展信息变更时 |
使用原则
- 指定更新字段:只有在
updateFieldList
中指定的字段才会被更新 - 保持原值:未指定的字段将保持Session中的原有值
- 提高效率:避免全量更新,减少不必要的数据传输
- 防止覆盖:避免意外覆盖不需要更新的字段
使用场景示例
1. 仅更新用户昵称
java
// 用户修改昵称时,只更新昵称字段
public SessionVo getLastSession() {
String userId = getCurrentUserId();
User user = userService.getById(userId);
SessionVo session = new SessionVo();
session.setNickname(user.getNickname()); // 设置新昵称
// 只更新昵称字段
List<String> updateFieldList = Arrays.asList("nickname");
session.setUpdateFieldList(updateFieldList);
return session;
}
2. 仅更新数据权限
java
// 用户的数据权限范围发生变化时,只更新数据权限
public SessionVo getLastSession() {
String userId = getCurrentUserId();
// 获取用户最新的数据权限
Map<String, Set<String>> dataAuthorizeMap = new HashMap<>();
// 获取组织权限
Set<String> orgIds = dataPermissionService.getUserOrgPermissions(userId);
dataAuthorizeMap.put("USER_DATA", orgIds);
// 获取商户权限
Set<String> merchantIds = dataPermissionService.getUserMerchantPermissions(userId);
dataAuthorizeMap.put("ORDER_DATA", merchantIds);
SessionVo session = new SessionVo();
session.setDataAuthorizeMap(dataAuthorizeMap);
// 只更新数据权限字段
List<String> updateFieldList = Arrays.asList("dataAuthorizeMap");
session.setUpdateFieldList(updateFieldList);
return session;
}
3. 批量更新多个字段
java
// 用户角色发生重大变更时,更新多个相关字段
public SessionVo getLastSession() {
String userId = getCurrentUserId();
// 从数据库获取用户最新信息
User user = userService.getById(userId);
List<Role> roles = roleService.getByUserId(userId);
List<Permission> permissions = permissionService.getByUserId(userId);
SessionVo session = new SessionVo();
session.setIsSystem(user.getIsAdmin());
session.setAuthorizeCodeList(buildAuthorizeCodes(roles));
session.setAuthorizeUrlList(buildAuthorizeUrls(permissions));
session.setDataAuthorizeMap(buildDataAuthorizeMap(userId));
// 更新多个字段
List<String> updateFieldList = Arrays.asList(
"isSystem",
"authorizeCodeList",
"authorizeUrlList",
"dataAuthorizeMap"
);
session.setUpdateFieldList(updateFieldList);
return session;
}
注意事项
- 仅Redis模式支持:动态更新Session功能仅在Redis存储模式下有效
- 性能考虑:不要设置过短的更新间隔,避免频繁调用更新接口
- 接口安全:更新接口应该有适当的安全验证
- 错误处理:更新失败时会继续使用原有Session信息
- 数据一致性:确保更新接口返回的数据与实际权限状态一致
令牌使用说明 🎫
请求示例
shell
# 使用curl发送请求
curl -X GET \
-H "authorization: your-token-here" \
http://127.0.0.1:8080/demo/getInfo
最佳实践 💡
存储方式选择
- Redis:适合需要频繁更新的场景
- JWT:适合分布式系统,无状态管理
安全建议
- 使用HTTPS传输
- 设置合理的过期时间
- 定期更换密钥
性能优化
- 合理设置缓存策略
- 避免频繁的权限检查
- 优化会话存储结构
常见问题 ❓
会话失效
- 检查过期时间设置
- 验证续期配置
- 确认存储服务状态
权限验证失败
- 检查权限配置
- 验证令牌格式
- 确认用户权限列表
安全提示 ⚠️
CAUTION
- 妥善保管JWT密钥和加密密钥
- 定期清理过期会话
- 监控异常登录行为
- 及时更新权限配置