档案管理系统的权限控制必须基于RBAC(基于角色的访问控制)模型。该模型将用户与权限解耦,通过角色作为中间层,大幅降低权限维护复杂度。
标准的RBAC模型包含以下四个核心实体:
权限设计必须遵循最小权限原则,具体操作权限分为三个层级:
创建以下6张核心表,MySQL建表语句如下:
```sql -- 用户表 CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL COMMENT '登录账号', password VARCHAR(100) NOT NULL COMMENT '加密后的密码', real_name VARCHAR(50) NOT NULL COMMENT '真实姓名', department_id BIGINT NOT NULL COMMENT '所属部门ID', is_enabled TINYINT DEFAULT 1 COMMENT '是否启用:0-禁用,1-启用', created_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT='系统用户表'; -- 角色表 CREATE TABLE sys_role ( id BIGINT PRIMARY KEY AUTO_INCREMENT, role_code VARCHAR(50) UNIQUE NOT NULL COMMENT '角色编码', role_name VARCHAR(50) NOT NULL COMMENT '角色名称', description VARCHAR(200) COMMENT '角色描述' ) COMMENT='系统角色表'; -- 权限表 CREATE TABLE sys_permission ( id BIGINT PRIMARY KEY AUTO_INCREMENT, perm_code VARCHAR(100) UNIQUE NOT NULL COMMENT '权限标识', perm_name VARCHAR(50) NOT NULL COMMENT '权限名称', resource_type VARCHAR(20) NOT NULL COMMENT '资源类型:MENU-菜单,BUTTON-按钮,API-接口', resource_path VARCHAR(200) COMMENT '资源路径', parent_id BIGINT DEFAULT 0 COMMENT '父权限ID', sort_order INT DEFAULT 0 COMMENT '排序号' ) COMMENT='系统权限表'; -- 用户角色关联表 CREATE TABLE sys_user_role ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, role_id BIGINT NOT NULL, UNIQUE KEY uk_user_role (user_id, role_id) ) COMMENT='用户角色关联表'; -- 角色权限关联表 CREATE TABLE sys_role_permission ( id BIGINT PRIMARY KEY AUTO_INCREMENT, role_id BIGINT NOT NULL, perm_id BIGINT NOT NULL, UNIQUE KEY uk_role_perm (role_id, perm_id) ) COMMENT='角色权限关联表'; -- 部门表(用于数据权限控制) CREATE TABLE sys_department ( id BIGINT PRIMARY KEY AUTO_INCREMENT, dept_code VARCHAR(50) UNIQUE NOT NULL COMMENT '部门编码', dept_name VARCHAR(100) NOT NULL COMMENT '部门名称', parent_id BIGINT DEFAULT 0 COMMENT '父部门ID', full_path VARCHAR(500) COMMENT '部门全路径' ) COMMENT='部门表'; ```在Spring Boot项目中集成Spring Security 6.x,实现完整的认证授权流程。
在pom.xml中添加以下依赖:
```xml创建SecurityConfig.java配置文件:
```java @Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { private final JwtAuthenticationFilter jwtAuthFilter; private final UserDetailsServiceImpl userDetailsService; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/auth/login").permitAll() .requestMatchers("/api/auth/register").permitAll() .requestMatchers("/api/admin/").hasRole("ADMIN") .requestMatchers("/api/archive/create").hasAuthority("archive:create") .requestMatchers("/api/archive/delete").hasAuthority("archive:delete") .anyRequest().authenticated()) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager( AuthenticationConfiguration config) throws Exception { return config.getAuthenticationManager(); } } ```实现用户详情查询服务,从数据库加载用户权限:
```java @Service @RequiredArgsConstructor public class UserDetailsServiceImpl implements UserDetailsService { private final UserMapper userMapper; private final RoleMapper roleMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 查询用户基本信息 SysUser user = userMapper.selectByUsername(username); if (user == null) { throw new UsernameNotFoundException("用户不存在"); } // 查询用户角色和权限 List使用JWT实现无状态认证,避免Session存储。
创建JwtUtil.java处理令牌生成与验证:
```java @Component public class JwtUtil { @Value("${jwt.secret}") private String secret; @Value("${jwt.expiration}") private Long expiration; // 生成令牌 public String generateToken(String username, List创建过滤器拦截请求并验证JWT:
```java @Component @RequiredArgsConstructor public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtUtil jwtUtil; private final UserDetailsServiceImpl userDetailsService; @Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { String token = authHeader.substring(7); if (jwtUtil.validateToken(token)) { String username = jwtUtil.getUsernameFromToken(token); UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } } filterChain.doFilter(request, response); } } ```
通过MyBatis拦截器实现部门级数据隔离。
创建ThreadLocal存储当前用户的数据权限范围:
```java public class DataPermissionContext { private static final ThreadLocal创建拦截器自动添加数据权限SQL:
```java @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}) }) @Component public class DataPermissionInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler handler = (StatementHandler) invocation.getTarget(); MetaObject metaObject = SystemMetaObject.forObject(handler); // 获取原始SQL MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); String sql = (String) metaObject.getValue("delegate.boundSql.sql"); // 检查是否需要数据权限过滤 if (needDataPermission(mappedStatement)) { SetVue.js前端权限控制方案。
创建自定义指令控制按钮显示:
```javascript // permission.js const permission = { mounted(el, binding) { const { value } = binding const permissions = store.getters.permissions if (value && value instanceof Array && value.length > 0) { const requiredPermissions = value const hasPermission = permissions.some(perm => { return requiredPermissions.includes(perm) }) if (!hasPermission) { el.parentNode && el.parentNode.removeChild(el) } } else { throw new Error(`需要权限标识,如 v-permission="['archive:create']"`) } } } // main.js中注册指令 app.directive('permission', permission) ```根据用户权限动态生成路由:
```javascript // 路由守卫中处理动态路由 router.beforeEach(async (to, from, next) => { if (store.getters.token) { if (store.getters.permissions.length === 0) { try { // 获取用户权限 const { permissions } = await store.dispatch('user/getUserInfo') // 生成可访问路由 const accessRoutes = await store.dispatch('permission/generateRoutes', permissions) // 动态添加路由 accessRoutes.forEach(route => { router.addRoute(route) }) next({ ...to, replace: true }) } catch (error) { next('/login') } } else { next() } } else { next() } }) ```解决权限修改后需要重新登录的问题。
使用Redis缓存用户权限,支持实时更新:
```java @Service public class PermissionCacheService { @Autowired private RedisTemplate当管理员修改用户权限时,发送WebSocket通知:
```java @RestController @RequestMapping("/api/admin/permission") public class PermissionAdminController { @Autowired private WebSocketServer webSocketServer; @PostMapping("/update") public Result updateUserPermissions(@RequestBody PermissionUpdateDTO dto) { // 1. 更新数据库权限 permissionService.updateUserPermissions(dto.getUserId(), dto.getPermissions()); // 2. 清除Redis缓存 permissionCacheService.clearUserPermissions(dto.getUserId()); // 3. 通知前端刷新权限 webSocketServer.sendMessage( dto.getUserId(), "PERMISSION_UPDATED" ); return Result.success(); } } ```执行以下SQL初始化基础权限数据:
```sql -- 插入基础角色 INSERT INTO sys_role (role_code, role_name, description) VALUES ('admin', '系统管理员', '拥有所有权限'), ('archive_manager', '档案管理员', '档案管理相关权限'), ('archive_viewer', '档案查阅员', '档案查阅权限'); -- 插入基础权限 INSERT INTO sys_permission (perm_code, perm_name, resource_type, resource_path, parent_id) VALUES ('archive:create', '创建档案', 'BUTTON', '/archive/create', 0), ('archive:delete', '删除档案', 'BUTTON', '/archive/delete', 0), ('archive:view', '查看档案', 'API', '/api/archive/', 0), ('archive:export', '导出档案', 'BUTTON', '/archive/export', 0), ('system:user:manage', '用户管理', 'MENU', '/system/user', 0); -- 分配角色权限 INSERT INTO sys_role_permission (role_id, perm_id) SELECT r.id, p.id FROM sys_role r, sys_permission p WHERE r.role_code = 'admin'; INSERT INTO sys_role_permission (role_id, perm_id) SELECT r.id, p.id FROM sys_role r, sys_permission p WHERE r.role_code = 'archive_manager' AND p.perm_code IN ('archive:create', 'archive:view', 'archive:export'); ```application.yml核心配置:
```yaml server: port: 8080 spring: datasource: url: jdbc:mysql://