当前位置: 首页 > news >正文

权限管理模块

登录相关

  • 权限管理模块(基础版)
    • 模块设计与实现
    • 优化点:
  • 前后端用户验证
    • 实现方式
  • 常见的攻击手段及防御手段

权限管理模块(基础版)

RBAC(Role-Base Access Control,基于角色的访问控制):是权限管理的常用方案。
核心:通过用户 - 角色 - 权限的三层关联,灵活分配和管理权限。并不将用户和全新啊直接绑定。适合多用户多权限场景。

模块设计与实现

以Java + Spring Boot + Spring Security为例

1、实体设计(库表设计)
需要具备3个实体(用户、角色、权限),以及2个关系表(用户-角色、角色-权限)。

// 用户实体
@Data
public class User {private Long id;private String username;private String password; // 加密存储(如BCrypt)private Integer status; // 1-启用,0-禁用// 关联角色(一对多,通过user_role表)private List<Role> roles;
}// 角色实体
@Data
public class Role {private Long id;private String name; // 角色名称(如“系统管理员”)private String code; // 角色标识(如“ROLE_ADMIN”,用于Spring Security)// 关联权限(一对多,通过role_permission表)private List<Permission> permissions;
}// 权限实体
@Data
public class Permission {private Long id;private String name; // 权限名称(如“删除用户”)private String code; // 权限标识(如“user:delete”,用于权限校验)private Integer type; // 1-菜单,2-按钮(接口)private String url; // 接口URL(如“/api/user/delete”)private Long parentId; // 父权限ID(用于菜单层级)
}
-- 用户-角色关联表
CREATE TABLE `user_role` (`id` bigint NOT NULL AUTO_INCREMENT,`user_id` bigint NOT NULL,`role_id` bigint NOT NULL,PRIMARY KEY (`id`),KEY `idx_user` (`user_id`),KEY `idx_role` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;-- 角色-权限关联表
CREATE TABLE `role_permission` (`id` bigint NOT NULL AUTO_INCREMENT,`role_id` bigint NOT NULL,`perm_id` bigint NOT NULL,PRIMARY KEY (`id`),KEY `idx_role` (`role_id`),KEY `idx_perm` (`perm_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2、Mapper层设计,提供数据库基本操作。

public interface UserMapper {// 查询用户User selectByUsername(String username);// 查询用户的角色List<Role> selectRolesByUserId(Long userId);// 查询角色的权限List<Permission> selectPermissionsByRoleId(Long roleId);
}
public Interface RolePermissionMapper() {// CRUD
}

3、Service实现,提供查询用户权限,用户与角色,角色与权限的分配与修改。
用户登录时,查询权限,用于后续权限校验。

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public List<String> login(String username,String password) {// 校验逻辑,判断用户是否存在return 用户信息(包含权限列表)}// 根据用户名查询用户及其关联的角色和权限public User getUserWithRolesAndPermissions(String username) {User user = userMapper.selectByUsername(username);if (user == null) {throw new UsernameNotFoundException("用户不存在");}// 查询用户关联的角色List<Role> roles = userMapper.selectRolesByUserId(user.getId());// 为每个角色查询关联的权限for (Role role : roles) {List<Permission> permissions = userMapper.selectPermissionsByRoleId(role.getId());role.setPermissions(permissions);}user.setRoles(roles);return user;}
}
// 管理用户-角色 角色-权限
@Service
public class RolePermissionService {@Autowiredprivate RolePermissionMapper rolePermissionMapper;// 给角色分配权限(先删除旧关联,再插入新关联)@Transactionalpublic void assignPermissions(Long roleId, List<Long> permIds) {// 1. 删除该角色已有的所有权限关联rolePermissionMapper.deleteByRoleId(roleId);// 2. 插入新的权限关联if (permIds != null && !permIds.isEmpty()) {List<RolePermission> list = permIds.stream().map(permId -> {RolePermission rp = new RolePermission();rp.setRoleId(roleId);rp.setPermId(permId);return rp;}).collect(Collectors.toList());rolePermissionMapper.batchInsert(list);}}
}

3、权限校验(限制到接口层面)
结合Spring Security实现接口访问时单独权限校验。
原理:

  • 将用户权限加载到上下文中。并通过注解或配置文件进行拦截。
  • Spring Security的授权流程通过 http.authorizeRequests() 对web请求进行授权保护。授权决策由 AccessDecisionManager 进行,它会对比当前访问资源所需的权限信息和用户信息中的权限信息。

步骤:

  1. 加载用户到Spring Security:
    实现UserDetailsService,将用户信息(包含角色和权限)转换为Security可识别的userDetails。
    UserDetailsService:从数据库中取出用户的账号密码。
@Service
public class CustomUserDetailsService implements UserDetailsService {@Autowiredprivate UserService userService;// 将用户信息以及对应的角色,权限交由Security管理@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 查询用户及其角色、权限User user = userService.getUserWithRolesAndPermissions(username);if (user == null) {throw new UsernameNotFoundException("用户不存在");}// 提取角色(需加前缀"ROLE_",符合Security规范)List<String> roleCodes = user.getRoles().stream().map(role -> "ROLE_" + role.getCode()).collect(Collectors.toList());// 提取权限标识(如"user:delete")List<String> permCodes = user.getRoles().stream().flatMap(role -> role.getPermissions().stream()).map(Permission::getCode).collect(Collectors.toList());// 合并角色和权限(Security将其统一视为"权限")List<String> authorities = new ArrayList<>();authorities.addAll(roleCodes);authorities.addAll(permCodes);// 返回Security用户对象。提供参数由Security转换用户与权限return org.springframework.security.core.userdetails.User.withUsername(user.getUsername()).password(user.getPassword()).authorities(authorities) // 角色和权限都作为authority.accountLocked(user.getStatus() == 0) // 状态为0时锁定.build();}
}
  1. 权限校验
  • 注解方式校验:在Controller方法上使用@PerAuthorize注解,指定所需要的权限。
@RestController
@RequestMapping("/api/user")
public class UserController {// 需拥有"user:delete"权限才能访问@PreAuthorize("hasAuthority('user:delete')")@DeleteMapping("/{id}")public Result deleteUser(@PathVariable Long id) {// 业务逻辑return Result.success();}// 需拥有"ROLE_ADMIN"角色才能访问@PreAuthorize("hasRole('ADMIN')") // 自动拼接"ROLE_"前缀@GetMapping("/list")public Result getUserList() {// 业务逻辑return Result.success();}
}
  • 配置列中通过URL匹配指定权限:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true) // 开启@PreAuthorize注解
public class SecurityConfig {@Autowiredprivate CustomUserDetailsService userDetailsService;@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(auth -> auth// 访问"/api/admin/**"需ADMIN角色.requestMatchers("/api/admin/**").hasRole("ADMIN")// 访问"/api/user/delete"需"user:delete"权限.requestMatchers("/api/user/delete/**").hasAuthority("user:delete")// 其他接口允许认证用户访问.anyRequest().authenticated());return http.build();}
}

4、前端权限适配(动态加载)
根据用户权限展示菜单和按钮。
原理:

  • 登陆后,获取当前用户权限列表(后端返回)
  • 渲染菜单/按钮时,只显示用户权限内的菜单/按钮。
// 1. 登录后获取用户权限
async function login(username, password) {const res = await api.login({ username, password });const { permissions } = res.data; // 后端返回的权限列表(如["user:delete", "menu:user"])localStorage.setItem('permissions', JSON.stringify(permissions));
}// 2. 权限判断工具函数
function hasPermission(permission) {const permissions = JSON.parse(localStorage.getItem('permissions') || '[]');return permissions.includes(permission);
}// 3. 动态渲染按钮(使用自定义指令)
Vue.directive('perm', {inserted(el, binding) {if (!hasPermission(binding.value)) {el.remove(); // 无权限则移除按钮}}
});// 4. 在模板中使用
<template><button v-perm="'user:delete'">删除用户</button>
</template>

优化点:

  • 角色继承:支持角色间的父子关系,需要在role表中添加Parent_id字段。
  • 数据权限:在功能权限基础上,控制数据可见范围(员工只能查看本人数据)。可通过在权限表中添加data_scope字段(限制全部/本部门/个人),结合SQL拦截器实现。
  • 权限缓存:可以将用户权限缓存到Redis中(key用户名,value权限列表),减少数据库压力,提高性能。(注意每次修改权限后需要考虑缓存一致性)。

前后端用户验证

交互中保证同一用户的核心:身份标识与验证机制。通过不同技术保证每次请求来自同一个用户,防止身份假冒以及会话混乱。

实现方式

1、 Cookie + Session的传统方案(适用于Web端)
最早且最成熟的方案,依赖服务器的会话存储和客户端的Cookie传递标识。
核心流程

  • 用户登录验证:
    • 登录验证通过后,在服务器内存/数据库中创建一个session(包含用户标识登录状态等信息),生成唯一的SESSIONID。
    • 后端通过Set-Cookie响应头返回客户端,客户端自动将其保存到Cookie中(通常设置HttpOnly和Secure属性)。
  • 后续请求身份确认
    • 客户端每次请求时,浏览器自动子啊请求头中Cookie字段按中携带SessionID。
    • 后端通过SessionID查询服务器的Session数据,若存在且有效(未过期),则任务是同一用户。

相关配置:

  • HttpOnly: true:禁止 JavaScript 读取 Cookie,防止 XSS 攻击窃取SessionID;
  • Secure: true:仅在 HTTPS 协议下传输 Cookie,避免明文泄露;
  • SameSite: Strict/Lax:限制 Cookie 跨域发送,防止 CSRF 攻击;
  • 会话超时机制:如 30 分钟无操作自动失效,降低SessionID被盗用的风险。

2、基于Token的无状态方案(适用于多端应用)
Token方案不依赖服务器存储会话,而是通过加密令牌传递用户信息,更适合前后端分离,移动端、小程序等场景。
核心流程

  • 生成Token:
    • 用户登陆验证通过后,后端使用密钥生成一个加密token(常见为JWT格式),包含用户Id、过期时间、签名等信息(不包含敏感数据)。
    • 后端将Token返回给客户端,客户端存储在LocalStorage、SessionStorage或App本地存储。
  • 携带Token请求
    • 客户端每次请求时,在HTTP请求头中(如Authorization:Bearer<.token>) 中携带token。
    • 后端验证Token签名(确保未篡改)和过期时间,解析出用户Id,确认是同一用户。
      JWT(JSON Web Token)示例:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VySWQiOjEsIm5hbWUiOiJKb2huIiwiZXhwIjoxNzIwMDAwMDAwfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
第一部分:算法声明(HS256);
第二部分:用户信息(用户 ID、过期时间);
第三部分:签名(用密钥生成,确保内容未被篡改)。

相关配置

  • 短期有效期:Token 过期时间设为 15-30 分钟,降低被盗用后的风险;
  • 刷新 Token 机制:同时返回access_token(短期)和refresh_token(长期,如 7 天),过期后用refresh_token重新获取access_token,避免频繁登录;
  • Token 黑名单:用户登出或密码修改时,将旧 Token 加入黑名单(Redis 存储),强制失效。

3、基于设备指纹与生物识别的增强方案(高安全性场景)
在金融、支付等安全性高的场景中,需要结合设备信息或生物特性辅助验证"同一用户”。

  • 设备指纹验证:
    • 后端收集客户端信息(CPU信息、操作系统等),生成唯一“设备指纹”
    • 登录时,将用户ID+设备指纹绑定,后续请求若设备指纹不符(如同一账号在新设备登录),触发二次验证(如短信验证)。
  • 生物识别辅助:
    • 移动端App可结合指纹识别,面部识别,通过后才允许携带Token请求敏感接口。
    • 即使Token泄露,没有生物特征同样无法完成操作。

4、跨域场景下身份有效。
当前后端域名不同,需要配置确保身份标识能跨域传递。

  • Cookie跨域:
    • 后端设置Access-Control-Allow-Credentials: true响应头;
    • 前端请求时携带credentials: 'include’参数(如 Axios 配置);
    • Cookie 设置domain: .parent.com(主域名一致时),允许子域名共享。
  • Token跨域
    • 无跨域限制,只在请求投中正确携带Token即可(不受Cookie同源策略影响)。

补充
Cookie同源策略:是浏览器的一种安全机制,用于防止不同源的网页之间相互访问数据,从而保护用户信息的安全。所谓“同源”,指的是两个网页的协议、域名和端口都相同。
作用:
同源策略的主要目的是防止恶意网站窃取用户数据。例如,如果用户登录了一个银行网站A,然后又访问了另一个网站B,如果没有同源策略,网站B可以读取网站A的Cookie,从而获取用户的敏感信息。

常见的攻击手段及防御手段

针对身份盗用的典型攻击(XSS、CSRF、重放攻击等),需要针对性防护。

1、防御XSS攻击(防止标识被窃取)
XSS攻击通过注入恶意脚本窃取前端存储的标识(如LocalStorage中的Token,document.cookie)。
预防:

  • 前端输入过滤:对用户输入内容(评论、表单)进行HTML转义(如>转义为& lt/ ); 使用框架自带的安全渲染(如React的JSX自动转义,VUE的v-text)。
  • 后端输出编码:返回给前端的数据中,对HTML/JS特殊字符编码,避免直接渲染未处理的用户输入。
  • 启用CSP(内容安全策略):

2、防御CSRF攻击(防止身份被滥用)
CSRF攻击:利用用户已登录的身份,诱导用户在第三方网站上发起恶意请求(如转账),防护措施:

  • SameSite Cookie:通过SameSite-Strict限制Cookie仅在同域请求中携带,彻底阻止跨域CSRF。
  • CSFR Token:对敏感操作(如表单操作、转账),后端生成随机CSFR Token(绑定Session),前端表单携带该Token,后端验证Token的有效性后才处理请求。
    例:前端表单隐藏字段,后端对比 Session 中的 Token。

3、防止重放攻击(防止标识被重复使用)
攻击者窃取Token后重复发送请求(如重复下单),防护措施:

  • Token短期有效+刷新时间
    • assess_token(访问令牌)有效期15~20分钟,用于日常请求;
    • refresh token(刷新令牌)有效期7天,用于过期后获取access_token,且刷新时验证设备信息(如设备信息);
    • 每次刷新后,旧access_token立即失效,refresh_token采用“一次性”机制(使用后立即失效,返回新的refresh_token)。
  • 请求时间戳+nonce
    前端请求时携带timestamp(当前时间戳)和nonce(随机字符串,仅用一次)后端验证:
    • 时间戳与服务器时间 不差5分钟(防止过期请求)。
    • nonce在Redis记录,已使用过的nonce直接拒绝(防止重复请求)。
http://www.dtcms.com/a/347206.html

相关文章:

  • 用 Ansible 优雅部署 Kubernetes 1.33.3(RedHat 10)
  • 第一章:启航篇 —— 新晋工程师的生存与扎根 (1)
  • TensorFlow 深度学习 开发环境搭建
  • 通过Java连接并操作MySQL数据库
  • 多智能体篇:智能体的“语言”——ACL协议与消息队列实现
  • 高斯分布的KL散度计算
  • STM32学习笔记19-FLASH
  • 标准浪涌测试波形对比解析
  • linux内核 - vmalloc 介绍
  • Unity 字符串输出文字一样但Equals 判断为false
  • 图论与最短路学习笔记
  • CH2 线性表
  • LeetCode 分类刷题:2529. 正整数和负整数的最大计数
  • IDEA控制台乱码(Tomcat)解决方法
  • 2-4.Python 编码基础 - 流程控制(判断语句、循环语句、break 语句与 continue 语句)
  • MySQL存储过程详解
  • `strlen` 字符串长度函数
  • GEO优化服务:智能时代的全球竞争新赛道
  • VS Code 中创建和开发 Spring Boot 项目
  • python企微发私信
  • Text2API与Text2SQL深度对比:自然语言驱动的数据交互革命
  • 【40页PPT】数据安全动态数据脱敏解决方案(附下载方式)
  • C/C++ 头文件命名约定
  • stack,queue以及deque的介绍
  • 【Java学习笔记】18.反射与注解的应用
  • [e3nn] 模型部署 | TorchScript JIT | `@compile_mode`装饰器 | Cython
  • TypeScript的构造函数constructor用法理解
  • 深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)第四章知识点问答补充及重新排版
  • 离线优先与冲突解决:ABP vNext + PWA 的边缘同步
  • SQL Server更改日志模式:操作指南与最佳实践!