安全认证体系
2025/12/3大约 7 分钟
安全认证体系
概述
本文档详细介绍 Gupt Management System 项目中安全认证体系的实现方案,基于 gupt-auth 模块的实际代码实现,包括身份认证、权限控制、JWT 令牌管理等关键技术点。
1. 技术架构
项目采用基于 Spring Security + JWT 的无状态认证架构,主要组件包括:
- Spring Security:核心安全框架,提供认证、授权基础支持
- JWT (JSON Web Token):无状态令牌机制,用于用户身份验证
- BCrypt:密码加密算法,确保密码安全存储
- 角色基础访问控制(RBAC):基于 STUDENT、TEACHER、ADMIN 三种主要角色
2. 核心组件实现
2.1 安全配置 (SecurityConfig)
安全配置类是整个认证体系的核心,负责配置安全规则、过滤器链和认证管理器。
@Slf4j
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@PostConstruct
public void init() {
SecurityConfig.log.info("✅ SecurityConfig loaded successfully!");
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
//配置放行
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/student/login", "/teacher/login", "/admin/login").permitAll()
// 仅拥有 STUDENT 角色的用户可以访问 /student/** 路径
.requestMatchers("/student/**").hasRole("STUDENT")
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/teacher/**").hasRole("TEACHER")
.anyRequest().authenticated()
)
.formLogin(AbstractHttpConfigurer::disable) // 禁用 Spring Security 默认表单登录
// 添加过滤器 addFilterBefore 在 UsernamePasswordAuthenticationFilter 之后执行
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
protected AuthenticationManager configure(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}主要特性:
- 基于路径的角色访问控制
- 禁用默认表单登录,使用自定义JWT认证
- 配置JWT过滤器在标准认证过滤器前执行
- 提供BCrypt密码编码器Bean
2.2 JWT认证过滤器 (JwtAuthenticationTokenFilter)
JWT认证过滤器负责拦截请求,提取并验证JWT令牌,实现无状态认证。
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private JwtProperties jwtProperties;
@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 忽略特定路径
String path = request.getRequestURI();
if ("/student/login".equals(path)) {
filterChain.doFilter(request, response);
return;
}
//获取token jwtProperties.getStudentToken()的名称需要跟前端传过来的名称一致
String token = request.getHeader(jwtProperties.getStudentToken());
if (StringUtils.isNotEmpty(token)) {
if (JwtTokenUtil.validateToken(token, jwtProperties.getStudentSecretKey())) {
//解析token
SecurityContextUtil.storeAuthentication(JwtTokenUtil.parseToken(token));
}
}
filterChain.doFilter(request, response);
}
}工作流程:
- 跳过登录路径,允许匿名访问
- 从请求头提取JWT令牌
- 验证令牌有效性
- 解析令牌内容并存储认证信息到安全上下文
2.3 JWT工具类 (JwtTokenUtil)
提供JWT令牌的生成、验证和解析功能。
@Component
public class JwtTokenUtil implements Serializable {
@Autowired
private JwtProperties jwtProperties;
// 生成 JWT Token
public String generateToken(List<? extends GrantedAuthority> authorities,Long id,String username) {
// 将角色信息转换为字符串列表
List<String> roles = authorities.stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
return JWT.create()
.withClaim("roles",roles)
.withClaim("id",id)
.withClaim("username",username)
.withIssuedAt(new Date())
//设置过期时间为一天
.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24))
.sign(Algorithm.HMAC256(jwtProperties.getStudentSecretKey()));
}
//验证token是否合法
public static Boolean validateToken(String token,String key) {
try {
JWT.require(Algorithm.HMAC256(key)).build().verify(token);
return true;
} catch (JWTVerificationException | IllegalArgumentException e) {
return false;
}
}
public static Authentication parseToken(String token) {
// 解码 token
DecodedJWT decodedJWT = JWT.decode(token);
Claim roles = decodedJWT.getClaim("roles");
// 检查 roles 是否为 null,如果是则返回一个空列表
List<String> roleList = roles.isNull() ? Collections.emptyList() : roles.asList(String.class);
// 获取角色信息并转换为 GrantedAuthority 列表
List<SimpleGrantedAuthority> authorities = roleList
.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
Long id = decodedJWT.getClaim("id").asLong();
String name = decodedJWT.getClaim("username").asString();
Student student = Student.builder()
.id(id)
.name(name)
.roleCode(roleList.get(0))
.roleNames(roleList.toString())
.build();
// 创建 StudentDetails 对象,包含用户信息和角色
StudentDetails studentDetails = StudentDetails.builder()
.student(student)
.build();
// 返回 Authentication 对象
return new UsernamePasswordAuthenticationToken(studentDetails, null, authorities);
}
}主要功能:
- 生成包含用户角色、ID和用户名的JWT令牌,有效期为24小时
- 验证令牌签名和有效性
- 解析令牌内容并构建Authentication对象
2.4 用户详情类 (StudentDetails)
实现Spring Security的UserDetails接口,为认证过程提供用户信息。
@Builder
public class StudentDetails implements UserDetails {
private final Student student;
public StudentDetails(Student student) {
this.student = student;
}
@Override
public List<? extends GrantedAuthority> getAuthorities() {
// 从学生对象中获取角色信息,并创建权限集合
return Collections.singletonList(new SimpleGrantedAuthority(student.getRoleNames()));
}
public Long getId() {
return student.getId();
}
@Override
public String getPassword() {
return student.getPassword();
}
@Override
public String getUsername() {
return student.getName();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public UserType getUserType() {
return UserType.STUDENT;
}
}特性:
- 封装学生用户信息
- 提供角色权限信息
- 实现Spring Security标准接口方法
- 支持用户状态判断
2.5 安全上下文工具 (SecurityContextUtil)
提供访问和管理Spring Security上下文的静态工具方法。
@Slf4j
public class SecurityContextUtil {
/**
* 存储当前用户信息到securityContextHolder
*/
public static void storeAuthentication(Authentication authentication) {
if (SecurityContextHolder.getContext() != null) {
SecurityContextHolder.getContext().setAuthentication(authentication);
}
else SecurityContextHolder.createEmptyContext().setAuthentication(authentication);
}
/**
* 获取当前登录用户详情
*/
public static UserDetails getUserDetails() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 检查是否已认证
if (authentication != null && authentication.isAuthenticated()) {
Object principal = authentication.getPrincipal();
// 检查 principal 是否是 UserDetails 实例
if (principal instanceof UserDetails) {
return (UserDetails) principal;
}
}
return null;
}
/**
* 获取当前登录用户
*/
public static UserDetails getCurrentUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
throw new RuntimeException("用户未登录");
}
Object principal = authentication.getPrincipal();
if (principal instanceof StudentDetails) {
return (StudentDetails) principal;
} else {
log.warn("未知的用户类型: {}", principal.getClass());
throw new IllegalStateException("未知的用户类型: " + principal.getClass());
}
}
/**
* 获取当前登录用户ID
*/
public static Long getCurrentUserId() {
UserDetails user = getCurrentUser();
if (user instanceof LoginUser) {
return ((LoginUser) user).getId();
} else if (user instanceof StudentDetails) {
return ((StudentDetails) user).getId();
} else {
throw new IllegalStateException("未知的用户类型: " + user.getClass());
}
}
/**
* 获取当前登录用户名称
*/
public static String getCurrentUserName() {
UserDetails user = getCurrentUser();
if (user instanceof LoginUser) {
return ((LoginUser) user).getUsername();
} else if (user instanceof StudentDetails) {
return ((StudentDetails) user).getUsername();
} else {
throw new IllegalStateException("未知的用户类型: " + user.getClass());
}
}
}主要功能:
- 存储认证信息到安全上下文
- 获取当前登录用户详情
- 获取当前用户ID、用户名等信息
- 支持多种用户类型的兼容处理
3. 认证流程
系统的认证流程设计如下:
用户登录:
- 用户通过
/student/login、/teacher/login或/admin/login端点提交凭证 - 后端验证凭证并生成JWT令牌
- 令牌包含用户ID、用户名和角色信息,有效期为24小时
- 用户通过
请求认证:
- 用户请求携带JWT令牌(通过请求头)
- JwtAuthenticationTokenFilter拦截请求,提取并验证令牌
- 验证通过后,将用户信息存入Spring Security上下文
- 后续的权限检查基于上下文中的用户角色信息
权限验证:
- 系统根据用户角色(STUDENT、TEACHER、ADMIN)控制资源访问
- 特定路径(如
/student/**)仅允许对应角色访问
4. 安全特性
4.1 密码安全
- 使用BCrypt算法对密码进行加密存储
- 支持密码强度验证
- 防止彩虹表攻击和密码明文泄露
4.2 令牌安全
- JWT令牌使用HMAC256算法签名
- 令牌有效期限制(24小时)
- 支持令牌验证和过期检查
4.3 访问控制
- 基于角色的URL访问控制
- 支持多种用户角色(学生、教师、管理员)
- 路径级别细粒度权限控制
5. 系统集成
5.1 配置依赖
安全认证模块通过以下方式与系统集成:
- 使用Spring Security作为核心框架
- 集成JWT处理库进行令牌管理
- 通过自动配置注册安全过滤器和认证组件
5.2 使用示例
在控制器中获取当前用户信息:
@RestController
@RequestMapping("/student")
public class StudentController {
@GetMapping("/profile")
public ResponseEntity<?> getStudentProfile() {
// 获取当前登录学生ID
Long studentId = SecurityContextUtil.getCurrentUserId();
String studentName = SecurityContextUtil.getCurrentUserName();
// 根据ID查询学生详细信息并返回
return ResponseEntity.ok("学生信息查询成功");
}
}6. 部署配置
6.1 配置项
安全认证相关的配置项通过JwtProperties类管理,主要包括:
- JWT密钥配置
- 令牌有效期设置
- 请求头中令牌字段名称
6.2 安全建议
- 生产环境中应使用强密钥并定期轮换
- 考虑实现令牌刷新机制
- 可根据需要调整令牌有效期
- 建议在API网关层进行初步的令牌验证
7. 监控与日志
安全模块内置了日志记录功能:
- 关键操作(如安全配置加载)的INFO级别日志
- 异常情况(如未知用户类型)的WARN级别日志
- 可结合系统日志框架进行统一日志管理和分析
总结
Gupt Management System 的安全认证体系基于 Spring Security 和 JWT 实现,提供了完整的用户认证、授权和会话管理功能。该实现采用无状态设计,支持多角色访问控制,并具备良好的安全性和可扩展性。通过合理的配置和使用,能够有效保障系统资源的安全访问。