Skip to content

权限验证拦截器 🔐

功能介绍 💡

TIP

权限验证拦截器就像一个智能门禁系统,负责管理用户的会话(Session)和权限验证。支持Redis和JWT两种验证方式,可以灵活应对不同的应用场景。

核心功能

  • ✅ 会话管理(Redis/JWT)
  • ✅ 权限验证
  • ✅ 动态更新session

详细配置说明 ⚙️

核心参数说明

参数名类型必填默认值说明
enabledbooleanfalse是否启用拦截器
rulesarray-规则列表

权限配置详解 (rules)

基础配置

参数名类型必填默认值说明
typestring-存储类型:redis/jwt
urlsarray-需要拦截的URL列表
authorization-namestringauthorization令牌参数名
business-keystringbearer业务标识(多系统区分用)
expireDuration-过期时间(如:30m)
is-generate-temp-codebooleanfalse是否生成临时码

Redis专用配置

参数名类型必填默认值说明
redis-auto-expirebooleanfalse是否自动延期

JWT专用配置

参数名类型必填默认值说明
jwt-secretstring-JWT密钥
is-cryptobooleanfalse是否启用加密
encrypt-typestringDES3加密算法类型,支持DES3、SM4
crypto-keystring-数据加密密钥(DES3算法要求24位,SM4算法要求16位)
jwt-check-logoutbooleanfalse是否验证退出状态

权限控制配置

参数名类型必填默认值说明
security-enabled-admin-authoritybooleanfalse是否启用管理员权限
security-visitor-urlsarray-游客可访问地址
security-login-urlsarray-登录后可访问地址
security-forbid-urlsarray-禁止访问地址

用户相关配置

参数名类型必填默认值说明
user-login-urlsarray-登录接口地址
user-logout-urlsarray-退出接口地址
user-get-info-urlsarray-获取用户信息接口
user-get-session-urlsarray-获取会话信息接口
user-login-typestring-用户登录类型,local 本地用户验证
accountsarray-本地用户配置列表
session-update-enabledbooleanfalse是否启用session更新(仅redis模式生效)
session-update-intervalDuration-session更新间隔时间,超过此时间自动更新session(如:10m)
session-update-urlstring-session更新的URI地址
session-update-timeoutDuration-session更新请求超时时间(如:30s)

本地用户配置[accounts]

参数名类型必填默认值说明
account-idstring-账户ID
account-nostring-账号
passwordstring-密码
nicknamestring-昵称
is-adminbooleanfalse是否管理员
authorize-urlsarray-授权的URL列表
authorize-codesarray-授权的权限码列表

配置示例 📋

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;
}

工作原理

  1. 触发条件:当用户访问受保护的URL时,网关会检查Session的更新时间
  2. 时间判断:如果距离上次更新超过了配置的session-update-interval时间
  3. 自动调用:网关会自动调用配置的session-update-url接口
  4. 更新Session:获取最新的Session信息并更新到Redis中
  5. 继续处理:使用更新后的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权限代码集合权限变更时
authorizeUrlListURL权限集合URL访问权限变更时
dataAuthorizeMap数据权限映射数据权限范围变更时
extendedMap拓展map扩展信息变更时

使用原则

  1. 指定更新字段:只有在updateFieldList中指定的字段才会被更新
  2. 保持原值:未指定的字段将保持Session中的原有值
  3. 提高效率:避免全量更新,减少不必要的数据传输
  4. 防止覆盖:避免意外覆盖不需要更新的字段

使用场景示例

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;
}

注意事项

  1. 仅Redis模式支持:动态更新Session功能仅在Redis存储模式下有效
  2. 性能考虑:不要设置过短的更新间隔,避免频繁调用更新接口
  3. 接口安全:更新接口应该有适当的安全验证
  4. 错误处理:更新失败时会继续使用原有Session信息
  5. 数据一致性:确保更新接口返回的数据与实际权限状态一致

令牌使用说明 🎫

请求示例

shell
# 使用curl发送请求
curl -X GET \
  -H "authorization: your-token-here" \
  http://127.0.0.1:8080/demo/getInfo

最佳实践 💡

  1. 存储方式选择

    • Redis:适合需要频繁更新的场景
    • JWT:适合分布式系统,无状态管理
  2. 安全建议

    • 使用HTTPS传输
    • 设置合理的过期时间
    • 定期更换密钥
  3. 性能优化

    • 合理设置缓存策略
    • 避免频繁的权限检查
    • 优化会话存储结构

常见问题 ❓

  1. 会话失效

    • 检查过期时间设置
    • 验证续期配置
    • 确认存储服务状态
  2. 权限验证失败

    • 检查权限配置
    • 验证令牌格式
    • 确认用户权限列表

安全提示 ⚠️

CAUTION

  1. 妥善保管JWT密钥和加密密钥
  2. 定期清理过期会话
  3. 监控异常登录行为
  4. 及时更新权限配置

相关文档 📚