最近需要使用Security认证框架,网上也有挺多相关文章,结合大家的分享加上一些我自己的见解,搭建了一个比较完整的dome,有需要的可以到git上拷贝。
git地址:https://gitee.com/wnmemory/security_demo.git
RBAC 模型
RBAC(Role-Based Access Control,基于角色的访问控制)是当前最流行的权限管理模型之一。它通过“用户 → 角色 → 权限”的映射关系,简化权限分配、降低管理成本,并支持灵活扩展。
| 组成 | 说明 | 举例 |
|---|---|---|
| 用户(User) | 系统中的主体,通常是登录账户 | 张三、李四 |
| 角色(Role) | 一组权限的集合,代表某种身份或职责 | 管理员、普通用户、财务 |
| 权限(Permission) | 对资源的操作许可 | 删除订单、查看报表、添加用户 |
| 资源(Resource) | 被访问的对象 | 订单、报表、用户菜单 |
RBAC 关键设计表结构
CREATE TABLE `user` (
`id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户ID,主键',
`username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户名,唯一',
`password` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密码',
`phone` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '手机号,唯一',
`nickname` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '昵称',
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '头像URL',
`gender` tinyint DEFAULT NULL COMMENT '性别:0-未知,1-男,2-女',
`status` tinyint DEFAULT '1' COMMENT '状态:0-禁用,1-正常',
`is_deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标志:0-未删除,1-已删除',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `phone` (`phone`),
KEY `idx_username` (`username`),
KEY `idx_phone` (`phone`),
KEY `idx_is_deleted` (`is_deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
CREATE TABLE `role` (
`id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色ID,主键,使用字符串形式',
`role` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色名称',
`role_code` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色编码,唯一',
`status` tinyint DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
`created_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人账号',
`updated_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '修改人账号',
`is_deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标志:0-未删除,1-已删除',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_role_code` (`role_code`),
KEY `idx_role` (`role`),
KEY `idx_status` (`status`),
KEY `idx_created_by` (`created_by`),
KEY `idx_updated_by` (`updated_by`),
KEY `idx_is_deleted` (`is_deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色表';
CREATE TABLE `user_role` (
`id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '映射ID,主键,使用字符串形式',
`user_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户ID,外键关联user表',
`role_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色ID,外键关联role表',
`status` tinyint DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
`is_deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标志:0-未删除,1-已删除',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_role` (`user_id`,`role_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_role_id` (`role_id`),
KEY `idx_status` (`status`),
KEY `idx_is_deleted` (`is_deleted`),
CONSTRAINT `fk_user_role_role` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`),
CONSTRAINT `fk_user_role_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户-角色映射表';
CREATE TABLE `permission` (
`id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '权限ID,主键,使用字符串形式',
`code` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '权限代码,唯一',
`name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '权限名称',
`description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '权限描述',
`is_deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标志:0-未删除,1-已删除',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`status` tinyint(1) DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
PRIMARY KEY (`id`),
UNIQUE KEY `code` (`code`),
KEY `idx_code` (`code`),
KEY `idx_is_deleted` (`is_deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='权限表';
CREATE TABLE `role_permission` (
`id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '映射ID,主键,使用字符串形式',
`role_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色ID,外键关联role表',
`permission_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '权限ID,外键关联permission表',
`status` tinyint DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
`is_deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标志:0-未删除,1-已删除',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_role_permission` (`role_id`,`permission_id`),
KEY `idx_role_id` (`role_id`),
KEY `idx_permission_id` (`permission_id`),
KEY `idx_status` (`status`),
KEY `idx_is_deleted` (`is_deleted`),
CONSTRAINT `fk_role_permission_permission` FOREIGN KEY (`permission_id`) REFERENCES `permission` (`id`),
CONSTRAINT `fk_role_permission_role` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色-权限映射表';
用户认证与权限控制整体流程概览
登录认证
- 用户提交用户名密码 → 后端验证 → 生成 JWT → 返回给前端。
- 前端后续请求携带 JWT(放在
Authorization: Bearer <token>头中)。 - 后端通过 JWT 验证用户身份与权限,决定是否允许访问资源。
权限控制
- 请求到达 →
JwtTokenFilter拦截 → 提取并验证 JWT。 - 若验证通过 → 构建
Authentication对象 → 存入SecurityContext。 - Spring Security 根据
Authentication中的权限信息,判断是否有权访问资源。
框架主要类职责介绍
| 类名 | 作用 | 说明 |
|---|---|---|
| JwtUtil | JWT 工具类 | 负责生成、解析、验证 JWT,提取用户名和权限等信息。 |
| CustomUserDetails | 用户详情封装 | 实现 UserDetails 接口,封装用户实体、角色、权限,供 Spring Security 使用。 |
| CustomUserDetailsService | 用户详情服务 | 实现 UserDetailsService 接口,根据用户名加载用户信息(包括角色和权限)。 |
| JwtTokenFilter | JWT 过滤器 | 拦截请求,提取 JWT,验证并设置认证信息到 SecurityContext。 |
| JwtAuthenticationEntryPoint | 认证失败处理器 | 当未认证用户访问受保护资源时,返回 401 Unauthorized。 |
| SecurityConfig | 安全配置中心 | 配置认证管理器、密码加密、过滤器链、放行规则等。 |
关键代码介绍
SecurityConfig类
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()//关闭 CSRF 防护(跨站请求伪造)
.exceptionHandling().authenticationEntryPoint(entryPoint).and()//当用户访问受保护资源但未认证或 token 无效时,由 entryPoint(即 JwtAuthenticationEntryPoint)处理,返回 401 Unauthorized
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()//设置会话策略为无状态,完全依赖 JWT 进行身份识别
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated();// /auth/** 路径下的所有请求(如登录、注册)直接放行,不需要认证;其他所有路径必须认证(即必须携带有效 JWT)
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);//将自定义的 JwtTokenFilter 添加到过滤器链中
}
UserDetailsService类
加载用户信息、角色、权限
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在: " + username);
}
if (user.getStatus() != 1) {
throw new UsernameNotFoundException("用户已被禁用: " + username);
}
// 获取用户角色
List<String> roleIds = userRoleMapper.findRoleIdsByUserId(user.getId());
List<Role> roles = roleMapper.listByIds(roleIds);
// 获取角色对应的权限
List<String> permissionIds = rolePermissionMapper.findPermissionIdsByRoleIds(roleIds);
List<Permission> permissions = permissionMapper.listByIds(permissionIds);
return new CustomUserDetails(user, roles, permissions);
}
UserDetails类
把当前用户的“角色”和“权限”统一转换成 Spring Security 能识别的“授权对象”集合(GrantedAuthority),供后续权限控制使用
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> set = new HashSet<>();
roles.forEach(r -> set.add(new SimpleGrantedAuthority(r.getRoleCode())));
permissions.forEach(p -> set.add(new SimpleGrantedAuthority(p.getCode())));
return set;
}
JwtUtil类
生成、解析、验证 token
public String generateToken(String username, List<String> authorities) {
Date expiry = new Date(System.currentTimeMillis() + expiration);//计算 token 的过期时间
return Jwts.builder()
.setSubject(username)//将用户名作为 token 的“主体”,后续可根据这个字段识别用户身份
.claim("authorities", authorities)//将用户的权限列表作为自定义声明(claim)存入 token
.setIssuedAt(new Date())//设置 token 的签发时间,用于判断 token 是否过期或是否有效
.setExpiration(expiry)//设置 token 的过期时间
.signWith(SignatureAlgorithm.HS512, secret)//使用 HS512 算法和配置的密钥(secret)对 token 进行签名,防止篡改
.compact();
}
JwtTokenFilter类
在每次请求到达时,拦截并解析 JWT,若合法则自动完成用户认证,把用户信息放入 Spring Security 上下文,使后续业务层/控制器能够拿到当前登录用户
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) { //只处理 Bearer <token> 格式的 JWT;否则直接放过,留给后面的过滤器或 Spring Security 处理
String token = header.substring(7);//去掉 "Bearer " 前缀,拿到纯 token 字符串
if (jwtUtil.validate(token)) { //用 JwtUtil 验证签名、过期时间等;不合法就什么都不做,请求继续(后面会因为 SecurityContext 里没认证信息而被拦截)
String username = jwtUtil.getUsername(token);
UserDetails details = userDetailsService.loadUserByUsername(username);//根据用户名加载完整的用户信息(包含角色、权限等)
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(details, null, details.getAuthorities());//构造 已认证 的 Authentication 对象
SecurityContextHolder.getContext().setAuthentication(auth);//把认证信息写入 Spring Security 上下文(SecurityContext);当前线程的任何地方都可以通过 SecurityContextHolder.getContext().getAuthentication() 拿到用户信息
}
}
chain.doFilter(request, response);//过滤器链继续向下执行,业务控制器最终拿到请求
}
权限控制方式
基于 URL 的权限控制(在 SecurityConfig 中配置)
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
基于注解的权限控制(在controller方法接口上加)
需要在SecurityConfig开启注解支持:@EnableGlobalMethodSecurity(prePostEnabled = true)
@PreAuthorize("hasRole('ADMIN')")//角色层面
public void someAdminOnlyMethod() { ... }
@PreAuthorize("hasAuthority('user:list')")//权限层面
public Result<PageInfo<User>> list(@RequestBody UserDto dto){ ... }
上述主要介绍了框架的重点内容,然后按自己的需求进行功能接口的设计编写就可以。
这是一个我个人编写的觉得比较完整的demo:提供注册,账密登录,电话登录,接口token校验,权限认证采用了接口级别,还有用户,角色,权限的增删查改等功能。
1213

被折叠的 条评论
为什么被折叠?



