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

SpringBoot实现权限管理系统完整指南(附源码)

一、权限系统核心概念

1.1 权限模型对比

模型类型特点适用场景
ACL直接定义用户-资源权限关系简单系统、资源数量少
RBAC通过角色关联权限,用户关联角色企业级应用(本系统采用)
ABAC基于属性动态计算权限复杂权限规则、云环境

1.2 RBAC核心元素

1
*
1
*
User
+String username
+String password
+List<Role> roles
Role
+String name
+List<Permission> perms
Permission
+String resource
+String operation
+String code

二、项目搭建与配置

2.1 初始化SpringBoot项目

Maven关键依赖

<dependencies><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!-- 数据库 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- 工具类 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- JWT支持 --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>
</dependencies>

2.2 数据库设计

2.2.1 用户表
CREATE TABLE `sys_user` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`username` varchar(50) NOT NULL COMMENT '用户名',`password` varchar(100) NOT NULL COMMENT '密码',`salt` varchar(20) DEFAULT NULL COMMENT '盐',`status` tinyint(4) DEFAULT '1' COMMENT '状态 0-禁用 1-正常',`create_time` datetime DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`),UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.2.2 权限表
CREATE TABLE `sys_permission` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`name` varchar(50) NOT NULL COMMENT '权限名称',`code` varchar(100) NOT NULL COMMENT '权限标识',`type` tinyint(4) NOT NULL COMMENT '类型 1-菜单 2-按钮 3-API',`url` varchar(200) DEFAULT NULL COMMENT '访问路径',`parent_id` bigint(20) DEFAULT NULL COMMENT '父级ID',`order_num` int(11) DEFAULT '0' COMMENT '排序',PRIMARY KEY (`id`),UNIQUE KEY `uk_code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

三、核心实现代码

3.1 安全配置类

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsServiceImpl userDetailsService;@Autowiredprivate JwtAuthenticationFilter jwtAuthenticationFilter;@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/auth/login").permitAll().antMatchers("/swagger**/**").permitAll().anyRequest().authenticated();// 添加JWT过滤器http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}

3.2 JWT工具类

public class JwtUtil {private static final String SECRET_KEY = "your-secret-key";private static final long EXPIRATION = 86400000; // 24小时public static String generateToken(UserDetails userDetails) {Map<String, Object> claims = new HashMap<>();return Jwts.builder().setClaims(claims).setSubject(userDetails.getUsername()).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)).signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();}public static String getUsernameFromToken(String token) {return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getSubject();}public static boolean validateToken(String token, UserDetails userDetails) {final String username = getUsernameFromToken(token);return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));}private static boolean isTokenExpired(String token) {final Date expiration = getExpirationDateFromToken(token);return expiration.before(new Date());}private static Date getExpirationDateFromToken(String token) {return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getExpiration();}
}

3.3 权限控制实现

3.3.1 自定义权限注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermission {String[] value();Logical logical() default Logical.AND;
}public enum Logical {AND, OR
}
3.3.2 权限验证切面
@Aspect
@Component
public class PermissionAspect {@Autowiredprivate PermissionService permissionService;@Before("@annotation(requiresPermission)")public void before(RequiresPermission requiresPermission) {String[] permissions = requiresPermission.value();Logical logical = requiresPermission.logical();// 获取当前用户权限Set<String> userPermissions = permissionService.getCurrentUserPermissions();if (logical == Logical.AND) {for (String permission : permissions) {if (!userPermissions.contains(permission)) {throw new AccessDeniedException("无权限访问");}}} else {boolean hasAny = false;for (String permission : permissions) {if (userPermissions.contains(permission)) {hasAny = true;break;}}if (!hasAny) {throw new AccessDeniedException("无权限访问");}}}
}

3.4 动态权限数据源

@Component
public class DynamicPermissionService implements FilterInvocationSecurityMetadataSource {@Autowiredprivate PermissionMapper permissionMapper;@Overridepublic Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {String requestUrl = ((FilterInvocation) object).getRequestUrl();// 从数据库加载所有权限配置List<Permission> permissions = permissionMapper.findAll();for (Permission permission : permissions) {if (antPathMatcher.match(permission.getUrl(), requestUrl)) {return SecurityConfig.createList(permission.getCode());}}// 没有匹配到默认需要登录return SecurityConfig.createList("ROLE_LOGIN");}@Overridepublic Collection<ConfigAttribute> getAllConfigAttributes() {return null;}@Overridepublic boolean supports(Class<?> clazz) {return FilterInvocation.class.isAssignableFrom(clazz);}
}

四、业务功能实现

4.1 用户登录接口

@RestController
@RequestMapping("/auth")
public class AuthController {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate JwtUtil jwtUtil;@PostMapping("/login")public Result login(@RequestBody LoginRequest request) {try {Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getUsername(),request.getPassword()));SecurityContextHolder.getContext().setAuthentication(authentication);UserDetails userDetails = (UserDetails) authentication.getPrincipal();String token = jwtUtil.generateToken(userDetails);return Result.success(token);} catch (AuthenticationException e) {return Result.error("用户名或密码错误");}}
}

4.2 用户权限服务

@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate RoleMapper roleMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 1. 查询用户User user = userMapper.findByUsername(username);if (user == null) {throw new UsernameNotFoundException("用户不存在");}// 2. 查询角色和权限List<Role> roles = roleMapper.findByUserId(user.getId());List<GrantedAuthority> authorities = new ArrayList<>();for (Role role : roles) {authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getCode()));for (Permission permission : role.getPermissions()) {authorities.add(new SimpleGrantedAuthority(permission.getCode()));}}return new org.springframework.security.core.userdetails.User(user.getUsername(),user.getPassword(),user.getStatus() == 1,true, true, true,authorities);}
}

五、前端接口示例

5.1 权限树形结构接口

@RestController
@RequestMapping("/api/permission")
public class PermissionController {@Autowiredprivate PermissionService permissionService;@GetMapping("/tree")@RequiresPermission("permission:view")public Result getPermissionTree() {List<PermissionNode> tree = permissionService.getPermissionTree();return Result.success(tree);}
}@Service
public class PermissionServiceImpl implements PermissionService {@Autowiredprivate PermissionMapper permissionMapper;@Overridepublic List<PermissionNode> getPermissionTree() {List<Permission> allPermissions = permissionMapper.findAll();// 构建树形结构return buildTree(allPermissions, 0L);}private List<PermissionNode> buildTree(List<Permission> permissions, Long parentId) {List<PermissionNode> nodes = new ArrayList<>();for (Permission permission : permissions) {if (permission.getParentId() != null && permission.getParentId().equals(parentId)) {PermissionNode node = new PermissionNode();BeanUtils.copyProperties(permission, node);node.setChildren(buildTree(permissions, permission.getId()));nodes.add(node);}}return nodes;}
}

5.2 角色分配权限接口

@PostMapping("/role/assign")
@RequiresPermission("role:assign")
public Result assignPermissions(@RequestBody RolePermissionRequest request) {roleService.assignPermissions(request.getRoleId(), request.getPermissionIds());return Result.success();
}@Service
@Transactional
public class RoleServiceImpl implements RoleService {@Autowiredprivate RolePermissionMapper rolePermissionMapper;@Overridepublic void assignPermissions(Long roleId, List<Long> permissionIds) {// 1. 删除原有权限rolePermissionMapper.deleteByRoleId(roleId);// 2. 添加新权限if (!CollectionUtils.isEmpty(permissionIds)) {List<RolePermission> rolePermissions = permissionIds.stream().map(permissionId -> {RolePermission rp = new RolePermission();rp.setRoleId(roleId);rp.setPermissionId(permissionId);return rp;}).collect(Collectors.toList());rolePermissionMapper.batchInsert(rolePermissions);}}
}

六、系统扩展与优化

6.1 权限缓存优化

@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())).entryTtl(Duration.ofHours(1));return RedisCacheManager.builder(factory).cacheDefaults(config).build();}
}@Service
public class PermissionServiceImpl implements PermissionService {@Cacheable(value = "userPermissions", key = "#userId")@Overridepublic Set<String> getUserPermissions(Long userId) {// 数据库查询逻辑}@CacheEvict(value = "userPermissions", key = "#userId")@Overridepublic void clearUserPermissionsCache(Long userId) {// 清除缓存}
}

6.2 操作日志切面

@Aspect
@Component
public class LogAspect {@Autowiredprivate LogService logService;@AfterReturning(pointcut = "@annotation(loggable)", returning = "result")public void afterReturning(JoinPoint joinPoint, Loggable loggable, Object result) {saveLog(joinPoint, loggable, null);}@AfterThrowing(pointcut = "@annotation(loggable)", throwing = "ex")public void afterThrowing(JoinPoint joinPoint, Loggable loggable, Exception ex) {saveLog(joinPoint, loggable, ex.getMessage());}private void saveLog(JoinPoint joinPoint, Loggable loggable, String errorMsg) {// 获取请求信息HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();// 构造日志对象SysLog log = new SysLog();log.setOperation(loggable.value());log.setMethod(joinPoint.getSignature().getName());log.setParams(JsonUtils.toJson(joinPoint.getArgs()));log.setIp(IPUtils.getIpAddr(request));log.setStatus(errorMsg == null ? 1 : 0);log.setErrorMsg(errorMsg);// 异步保存日志logService.saveLog(log);}
}

七、项目部署与测试

7.1 测试用例示例

@SpringBootTest
public class AuthServiceTest {@Autowiredprivate AuthService authService;@Testpublic void testLoginSuccess() {LoginRequest request = new LoginRequest("admin", "123456");Result result = authService.login(request);assertTrue(result.isSuccess());assertNotNull(result.getData());}@Testpublic void testLoginFail() {LoginRequest request = new LoginRequest("admin", "wrong-password");Result result = authService.login(request);assertFalse(result.isSuccess());}
}

7.2 接口测试(Postman)

登录请求

POST /auth/login
Content-Type: application/json{"username": "admin","password": "123456"
}

响应示例

{"code": 200,"message": "success","data": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTYy..."
}

访问受保护接口

GET /api/user/list
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

结语

本系统完整实现了基于SpringBoot的RBAC权限管理系统,具有以下特点:

  1. 采用JWT实现无状态认证
  2. 支持动态权限配置
  3. 精细化的权限控制(方法级+URL级)
  4. 完善的缓存机制

扩展建议

  • 增加多因素认证(短信/邮箱验证码)
  • 实现权限变更实时生效(WebSocket推送)
  • 添加权限变更历史记录

完整项目源码已托管至GitHub:项目地址

通过本系统,开发者可以快速构建安全可靠的企业级应用,并根据实际需求进行个性化扩展。

http://www.xdnf.cn/news/14060.html

相关文章:

  • 【Code】COP FOR THE STRUCTURAL USE OF STEEL 2011 (2023 Edition)
  • SDPA(Scaled Dot-Product Attention)详解
  • CE 标志新门槛:智能门锁 EN 18031 认证合规路径与成本分析
  • 分布式锁模板工具类
  • 2025企业级BI产品评测和推荐
  • 在 WinForms 中制作无边框窗体通过鼠标拖动移动和调整大小,难点是我窗体上被标题栏和状态栏dock之后很难选中
  • 短视频矩阵系统开发实战:PHP实现SaaS独立部署
  • deepimagej-plugin开源程序是用于运行深度学习模型的 ImageJ 插件
  • 【解决方案】Kali 2022.3修复仓库密钥一键安装docker,docker compose
  • 开发环境与生产环境的 yml 文件与配置
  • 1分钟理解FreeRtos中的信号量知识
  • LeetCode - 387. 字符串中的第一个唯一字符
  • 一个简单的torch-cuda demo
  • Acrobat 首选项配置:从注册表到锁定机制
  • 【MPC】模型预测控制笔记 (2):约束MPC
  • C语言预处理命令详解
  • 第9章 表达式和运算符 笔记 待完善
  • 如何运营一个专业的体育比分网站
  • 2025年06月13日Github流行趋势
  • 【Photoshop】使用路径和形状制作印章
  • 基于51单片机的电机控制角度proteus仿真
  • 【IEEE/EI/Scopus检索】2025年第五届机器学习与大数据管理国际会议 (MLBDM 2025)
  • Streamlit 笔记
  • Google Drive·实现共享文件本地访问与编辑
  • sdk添加包,更新文件系统--万象奥科RK3506
  • 基于数字孪生的风光储一体化园区智能化管理解决方案
  • C++进阶—C++中的继承
  • 在云算力上使用Nginx对Gradio外链进行加速的方法
  • 趣解TensorFlow之入门篇
  • 家政维修平台实战25:工人接单