刷到一个挺有意思的话题,结合自己之前的经验,整理了一下核心要点。
文章目录
- 什么是 RBAC
- Spring Security 是什么
- 整体架构设计
- 第一步:定义用户模型和角色
- 第二步:密码加密
- 第三步:JWT 工具类
- 第四步:让 Spring Security 认识你的用户
- 第五步:JWT 认证过滤器
- 第六步:安全配置——定义规则
- 完整请求流程
什么是 RBAC
RBAC,全称 Role-Based Access Control,基于角色的访问控制。核心思路很简单:不直接给用户分配权限,而是引入"角色"作为中间层。
用户 →角色→ 权限
举个例子:一家公司里,"实习生"能查看文档,"正式员工"能查看和编辑文档,"部门经理"能查看、编辑、删除文档。你入职的时候,HR 不会一条条给你勾权限,而是直接给你一个角色,权限就跟着来了。
这里面有几个关键关系:
- 一个用户能够拥有多个角色
- 一个角色能够分配给多个用户
- 一个角色能够包含多个权限
- 一个权限可以属于多个角色
角色继承——上层角色自动继承下层角色的所有权限。比如"经理"自动拥有"员工"的全部权限,再额外拥有管理权限。这在组织架构繁琐的系统中非常实用。
Spring Security 是什么
Spring Security 本质上是一个基于 Filter 的安全框架。请求进入应用时,会经过一系列过滤器链,每个过滤器各司其职:有的负责认证,有的负责授权,有的负责 CSRF 防护。
它解决两个核心问题:
- 认证(Authentication):你是谁?验证用户名密码是否正确。
- 授权(Authorization):你能干什么?检查你有没有权限访问某个接口。
整体架构设计
要基于 Spring Security 实现 RBAC,需要这几个核心模块:
config/ → 安全配置、JWT 过滤器
controller/ → 接收请求的入口
service/ → 业务逻辑、用户详情加载
repository/ → 数据库访问
model/ → 用户实体、角色定义
utils/ → 密码加密、JWT 工具
下面按照请求的生命周期来讲,从注册到登录到鉴权,一步步走通。
第一步:定义用户模型和角色
@Data
@Entity
@Table(name = "wzxg_users", uniqueConstraints = @UniqueConstraint(columnNames = "username"))
public class WzxgUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String username;
@Column(nullable = false)
private String password;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private WzxgRole role;
@CreationTimestamp
private LocalDateTime createdTime;
@UpdateTimestamp
private LocalDateTime updatedTime;
public enum WzxgRole {
USER, ADMIN
}
}
@Enumerated(EnumType.STRING) 让角色以字符串形式存到数据库里,比如存的是 "ADMIN" 而不是 0,可读性好,也不容易出错。
数据访问层很简单,一个根据用户名查询的办法就够了:
public interface WzxgUserRepository extends JpaRepository {
Optional findByUsername(String username);
}
第二步:密码加密
密码绝对不能明文存储。BCrypt 是目前主流的选择,它内置盐值机制,同一个密码每次加密结果都不同,能有效防御彩虹表攻击。
public class WzxgPasswordUtil {
private static final BCryptPasswordEncoder wzxgEncoder = new BCryptPasswordEncoder();
public static String encode(String rawPassword) {
return wzxgEncoder.encode(rawPassword);
}
public static boolean matches(String rawPassword, String encodedPassword) {
return wzxgEncoder.matches(rawPassword, encodedPassword);
}
}
注册时调 encode(),登录时调 matches()
第三步:JWT 工具类
系统采用无状态认证,服务端不存 session,而是通过 JWT 令牌来传递身份信息。
(JWT相关可看Cookie、Session、Token、JWT开发流程详解)
@Component
public class WzxgJwtUtils {
private static final String WZXG_SECRET_KEY = "wzxg_jwt_secret_key_2024";
private static final long WZXG_EXPIRATION = 86400000; // 24小时
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + WZXG_EXPIRATION))
.signWith(SignatureAlgorithm.HS256, WZXG_SECRET_KEY)
.compact();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(WZXG_SECRET_KEY).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
public String extractUsername(String token) {
Claims wzxgClaims = Jwts.parser()
.setSigningKey(WZXG_SECRET_KEY)
.parseClaimsJws(token)
.getBody();
return wzxgClaims.getSubject();
}
}
JWT 是一个自包含的令牌,里面可以塞用户名、角色、组织信息等。这样每次请求过来,解析 token 就能知道用户是谁、有什么权限,不用查数据库。
第四步:让 Spring Security 认识你的用户
Spring Security 有自己的一套用户模型 UserDetails,我们需要实现 UserDetailsService 接口,把数据库里的用户转换成它能搞懂的格式。
```
@Service
public class WzxgUserDetailsService implements UserDetailsService {
@Autowired
private WzxgUserRepository wzxgUserRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
WzxgUser wzxgUser = wzxgUserRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
return new org.springframework.security.core.userdetails.User(
wzxgUser.getUsername(),
wzxgUser.getPassword(),
buildAuthorities(wzxgUser.getRole())
);
}
private Collection
暂时整理到这里。以上都是个人理解,可能有疏漏,欢迎指正。
评论 (0)
暂无评论