完整认证授权流程与权限控制实现

一、整体架构概览

认证授权流程 = 认证(Authentication) + 授权(Authorization)
├── 认证:验证"你是谁" → 身份确认
│   ├── 用户名密码验证
│   ├── Token生成与验证
│   └── 用户信息加载
└── 授权:验证"你能做什么" → 权限控制
    ├── 按钮权限控制:操作权限
    └── 数据权限控制:数据访问范围

二、用户登录认证流程

  1. 前端提交登录信息:用户在前端界面输入用户名和密码,前端将加密后的凭证发送到后端登录接口。

  2. 后端验证用户凭证:后端接收到登录请求后,通过用户服务验证用户名和密码。验证通过后,生成一个唯一的用户标识(通常是用户ID)和Token。

  3. 加载用户权限信息:登录成功后,后端会调用SysUserDetailServiceImpl.initUserData()方法,加载用户的按钮权限和数据权限。

    • 按钮权限:通过用户关联的角色,查询角色拥有的菜单权限(即权限标识,如sys:user:save),形成一个权限标识集合,存储到UserDetail对象的authoritySet字段。

    • 数据权限:通过用户关联的角色,查询角色定义的数据权限范围(部门ID列表),存储到UserDetail对象的deptIdList字段。

  4. 缓存用户信息:将包含权限信息的UserDetail对象缓存到Redis中,键为user:auth:{userId},并设置过期时间(如2小时)。同时,将权限列表单独缓存,以便快速获取。

  5. 生成Token并返回:后端生成一个JWT Token,其中包含用户ID和必要信息,并将Token返回给前端。前端将Token存储在本地(如localStorage或cookie),并在后续请求中携带。

三、业务请求授权流程

  1. 前端携带Token发送请求:前端在请求头中携带JWT Token,发送业务请求到后端。

  2. Token验证与用户信息获取:后端的认证过滤器(如AuthenticationTokenFilter)拦截请求,从请求头中提取Token,并验证Token的有效性。验证通过后,从Token中解析出用户ID,然后从Redis缓存中获取用户信息(即UserDetail对象)。如果缓存不存在,则重新加载用户信息并缓存。

  3. 设置安全上下文:将获取到的用户信息(包括权限信息)设置到Spring Security的SecurityContext中,这样在后续的业务逻辑中可以通过SecurityUtils.getUserDetail()获取当前用户信息。

  4. 按钮权限控制:在控制器方法上,使用@PreAuthorize("hasAuthority('权限标识')")注解。当请求进入控制器方法前,Spring Security会根据SecurityContext中的用户权限信息,判断用户是否拥有该注解指定的权限标识。如果有,则允许访问;否则,返回403错误。

  5. 数据权限控制:在服务层方法上,使用自定义的@DataScope注解标记需要进行数据权限过滤的方法。通过MyBatis拦截器(DataFilterInterceptor)拦截查询SQL,根据当前用户的部门ID列表(从UserDetail对象的deptIdList获取)重写SQL,在WHERE子句中添加部门ID过滤条件。这样,查询结果就只限于用户有权限访问的数据。

  6. 返回响应:业务逻辑执行完毕后,将结果返回给前端。

四、按钮权限控制实现详解

1. 权限标识体系设计

系统采用三层权限标识设计,确保权限控制的精确性:

  • 第一层:模块(sys) - 标识功能模块

  • 第二层:实体(user) - 标识操作对象

  • 第三层:操作(save) - 标识具体操作

2. 权限加载过程

用户登录时,系统通过以下步骤加载按钮权限:

  1. 查询用户角色:根据用户ID查询关联的角色列表

  2. 获取菜单权限:根据角色查询关联的菜单权限

  3. 提取权限标识:从菜单配置中提取permissions字段的权限标识

  4. 权限去重合并:多个角色的权限去重合并

  5. 缓存到Redis:权限集合缓存到Redis,key格式:menu:permissions:{userId}

3. 权限验证机制

3.1 后端验证(Spring Security)

在控制器方法上使用注解式权限控制:

@PreAuthorize("hasAuthority('sys:user:save')")
public Result saveUser() {
    // 只有拥有sys:user:save权限的用户能执行此方法
}

Spring Security的执行流程:

  1. 拦截请求:Spring Security过滤器链拦截请求

  2. 读取注解:解析控制器方法上的@PreAuthorize注解

  3. 获取权限:从SecurityContext中获取用户权限集合

  4. 验证权限:检查用户权限集合是否包含所需权限

  5. 决定放行/拒绝:验证通过则放行,否则抛出AccessDeniedException

3.2 前端验证(Vue + Pinia)

前端通过统一的权限验证方法控制UI元素显示:

// 检查权限的公共方法
hasPermission(permission) {
    return this.$store.state.user.permissions.includes(permission);
}

// 使用示例
<button v-if="hasPermission('sys:user:delete')">删除</button>

五、数据权限控制实现详解

1. 数据权限模型设计

系统采用基于角色的数据权限模型:

  • 角色数据范围:在sys_role_data_scope表中定义每个角色可访问的部门

  • 用户数据范围:用户拥有的所有角色数据范围的并集

  • 最终数据权限 = 用户所属部门 + 角色数据范围部门

2. 数据权限加载过程

用户登录时,系统通过以下步骤加载数据权限:

  1. 查询用户直接部门:从sys_user_dept表查询用户直接所属部门

  2. 查询角色数据范围:根据用户角色查询数据权限范围

  3. 合并部门列表:合并直接部门和角色数据范围部门,去重

  4. 缓存到Redis:部门ID列表缓存到Redis,key格式:data:scope:{userId}

3. 数据权限过滤机制

3.1 MyBatis拦截器实现

系统通过自定义MyBatis拦截器实现数据权限的自动注入:

拦截器工作流程

  1. 拦截SQL查询:拦截所有执行查询的Mapper方法

  2. 检查是否需要过滤:通过@DataScope注解标记需要数据过滤的方法

  3. 获取用户数据权限:从当前线程的UserContext获取部门ID列表

  4. 重写SQL语句:在WHERE条件中注入部门过滤条件

  5. 执行修改后的SQL:将重写后的SQL交给MyBatis执行

SQL重写示例

sql
-- 原始SQL
SELECT * FROM sys_user WHERE status = 1

-- 重写后的SQL(假设用户可访问部门1,2,3)
SELECT * FROM sys_user 
WHERE status = 1 
AND dept_id IN (1, 2, 3)

3.2 多租户数据隔离

在SaaS多租户系统中,数据权限还包含租户隔离:

sql
-- 重写后的SQL(同时包含租户隔离和数据权限)
SELECT * FROM sys_user 
WHERE status = 1 
AND tenant_id = 1001  -- 租户隔离
AND dept_id IN (1, 2, 3)  -- 数据权限

六、Redis在权限控制中的作用

1. Redis缓存结构设计

text
user:auth:{userId}          # 用户完整认证信息
menu:permissions:{userId}   # 按钮权限集合
data:scope:{userId}         # 数据权限部门列表
menu:tree:{userId}          # 用户菜单树
token:{jwtToken}            # Token与用户ID映射

2. 缓存读写策略

  • 写缓存(登录时)

    1. 查询数据库获取用户权限信息

    2. 将权限信息序列化存储到Redis

    3. 设置合理的过期时间(如2小时)

  • 读缓存(请求时)

    1. 从请求头获取Token

    2. 从Redis获取用户权限信息

    3. 如果缓存不存在,重新从数据库加载

  • 缓存失效策略

    1. 主动失效:用户权限变更时主动清除缓存

    2. 被动失效:设置TTL自动过期

    3. 强制失效:用户登出时清除相关缓存

七、总结

该系统的认证授权流程实现了完整的权限控制闭环:

  1. 认证流程:从用户登录到Token生成,确保用户身份安全可靠

  2. 按钮权限控制:通过权限标识体系,实现操作级别的精细控制

  3. 数据权限控制:通过SQL重写技术,实现数据访问范围的控制

  4. 缓存优化:利用Redis缓存权限信息,大幅提升系统性能

  5. 实时生效:通过事件驱动机制,保证权限变更及时生效

这种设计实现了多层次、细粒度、高性能的权限控制,既能满足企业级应用的安全要求,又能保证系统的响应速度和用户体验。通过前后端协同、数据库拦截器和缓存技术的结合,构建了一个完整、安全、高效的权限控制体系。

posted @ 2026-01-27 17:00  雨花阁  阅读(3)  评论(0)    收藏  举报