若依框架通过Token实现数据权限和接口权限过滤的机制

Administrator
Administrator
发布于 2025-04-30 / 23 阅读
0
0

若依框架通过Token实现数据权限和接口权限过滤的机制

若依(RuoYi)框架采用基于Token的认证授权机制,结合Spring Security和自定义逻辑实现数据权限和接口权限的过滤。以下是详细实现原理:

1. Token生成与识别机制

Token生成流程

  1. 用户登录成功时生成JWT Token:

// SysLoginService.java
public String login(String username, String password) {
    // 验证成功后生成Token
    String token = JwtUtils.createToken(loginUser.getUserId());
    // 将用户信息和权限缓存到Redis
    redisCache.setCacheObject("login_tokens:"+token, loginUser);
    return token;
}
  1. Token结构​:

  • 基于JJWT库生成的JWT Token

  • 包含用户ID、登录时间等基本信息

  • 不直接包含权限数据(权限数据存储在Redis)

Token识别流程

  1. 请求拦截​:

// JwtAuthenticationTokenFilter.java
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
    // 从请求头获取Token
    String token = getToken(request);
    if (StringUtils.isNotEmpty(token)) {
        // 解析Token获取用户ID
        String userid = JwtUtils.getUserid(token);
        // 从Redis获取用户完整信息
        LoginUser loginUser = redisCache.getCacheObject("login_tokens:"+token);
        // 将认证信息存入SecurityContext
        UsernamePasswordAuthenticationToken authentication = 
            new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }
    chain.doFilter(request, response);
}

2. 接口权限过滤实现

权限注解控制

// 使用Spring Security的PreAuthorize注解
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/list")
public TableDataInfo list(SysUser user) {
    // 方法实现
}

权限验证服务

// PermissionService.java (通过@ss引用)
public boolean hasPermi(String permission) {
    // 从SecurityContext获取当前用户权限
    Set<String> permissions = getLoginUser().getPermissions();
    return permissions.contains(permission);
}

3. 数据权限过滤实现

数据权限注解

// 在Mapper接口上添加@DataScope注解
@DataScope(deptAlias = "d", userAlias = "u")
List<SysUser> selectUserList(SysUser user);

实现原理

  1. AOP拦截​:

// DataScopeAspect.java
@Before("@annotation(dataScope)")
public void doBefore(DataScope dataScope) {
    // 获取当前用户的数据权限范围
    SysUser user = SecurityUtils.getLoginUser().getUser();
    String permission = StringUtils.format(" OR {}.dept_id IN ({}) ", 
            dataScope.deptAlias(), dataScopeService.getDeptIdsForDataScope(user));
    // 将权限SQL存入ThreadLocal
    DataScopeHelper.setDataScope(permission);
}
  1. SQL改写​:

// Mybatis拦截器自动将权限SQL拼接到查询中
@Intercepts({
    @Signature(type= StatementHandler.class, method="prepare", args={Connection.class, Integer.class})
})
public class DataScopeInterceptor implements Interceptor {
    public Object intercept(Invocation invocation) {
        // 获取原始SQL
        String sql = boundSql.getSql();
        // 添加数据权限过滤条件
        if (DataScopeHelper.hasDataScope()) {
            sql = sql + " AND " + DataScopeHelper.getDataScope();
        }
        // 执行修改后的SQL
    }
}

4. 权限数据加载流程

  1. 登录时加载​:

// SysLoginService.java
public LoginUser loadUserByUsername(String username) {
    // 查询用户基本信息
    SysUser user = userMapper.selectUserByUserName(username);
    // 查询用户角色和权限
    Set<String> permissions = permissionService.getMenuPermission(user);
    // 封装到LoginUser对象
    return new LoginUser(user, permissions);
}
  1. Redis缓存结构​:

login_tokens:token -> {
    "userId": 1,
    "user": {用户基本信息},
    "permissions": ["system:user:list", ...],
    "roles": ["admin", ...]
}

5. 安全性设计

  1. Token防篡改​:

  • 使用HS512算法签名

  • 签名密钥存储在配置文件中

  1. Token失效机制​:

# application.yml
token:
  header: Authorization    # 请求头名称
  secret: abcdefghijklmn   # 密钥
  expireTime: 720          # 过期时间(分钟)
  1. 并发控制​:

  • 同一用户多次登录会生成不同Token

  • 旧Token在有效期内仍可使用(可配置强制失效)

这种设计实现了若依框架的细粒度权限控制,既保证了接口访问的安全性,又实现了灵活的数据权限过滤。


评论