Spring AI Alibaba 基于JWT的鉴权登录系统实现详解
文章目录
- Spring AI Alibaba 基于JWT的鉴权登录系统实现详解
- 一、系统架构概述
- 二、核心组件分析
- 1. 用户实体设计
- 2. 角色和权限管理
- 3. Spring Security配置
- 三、JWT认证机制实现
- 1. JWT令牌工具类
- 2. JWT认证过滤器
- 四、用户认证流程
- 1. 登录处理
- 2. 用户详情加载服务
- 五、总结
Spring AI Alibaba 基于JWT的鉴权登录系统实现详解
采用了基于JWT(JSON Web Token)的无状态认证方案,实现了灵活、安全的用户登录和权限管理。本文将深入分析其实现机制。
一、系统架构概述
认证系统基于Spring Security框架构建,采用了以下核心组件:
- Spring Security:提供基础的认证和授权功能
- JWT:实现无状态的令牌认证
- Spring Data JPA:处理用户和角色数据的持久化
- BCrypt:进行密码加密和验证
二、核心组件分析
1. 用户实体设计
User实体类实现了Spring Security的UserDetails接口,使其成为认证系统的核心:
@Data
@Entity
@Table(name = "users")
public class User implements UserDetails {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(name = "username", nullable = false, unique = true, length = 50)private String username;@Column(name = "password", nullable = false, length = 255)private String password;@Column(name = "email", nullable = false, unique = true, length = 100)private String email;@Column(name = "enabled", nullable = false)private boolean enabled = true;@Column(name = "Status", nullable = false)private Integer Status ;@ManyToMany(fetch = FetchType.EAGER)@JoinTable(name = "user_roles",joinColumns = @JoinColumn(name = "user_id"),inverseJoinColumns = @JoinColumn(name = "role_id"))private Set<Role> roles = new HashSet<>();// 实现UserDetails接口的其他方法
}
2. 角色和权限管理
Role实体实现了GrantedAuthority接口,用于定义用户权限:
@Data
@Entity
@Table(name = "roles")
public class Role implements GrantedAuthority {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(name = "name", nullable = false, unique = true, length = 50)private String name;@Overridepublic String getAuthority() {// 确保角色名称以ROLE_前缀开始if (name != null && !name.startsWith("ROLE_")) {return "ROLE_" + name;}return name;}
}
3. Spring Security配置
SecurityConfig类是整个认证系统的核心配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig {@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate JwtAuthenticationFilter jwtAuthenticationFilter;@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {return authConfig.getAuthenticationManager();}@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.csrf(csrf -> csrf.disable()) // 禁用CSRF保护(适用于API).authorizeHttpRequests(auth -> auth// 允许匿名访问的端点.requestMatchers("/api/auth/register", "/api/auth/login", "/error").permitAll()// 允许匿名访问静态资源.requestMatchers("/", "/index.html", "/login.html", "/register.html", "/chat.html", "/static/**", "/css/**", "/js/**", "/img/**", "/favicon.ico").permitAll()// 所有其他请求需要认证.anyRequest().authenticated())// 配置无状态会话.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))// 添加JWT认证过滤器.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);return http.build();}
}
三、JWT认证机制实现
1. JWT令牌工具类
JwtTokenUtil类负责JWT令牌的生成、解析和验证:
@Component
public class JwtTokenUtil {private static final Logger logger = LoggerFactory.getLogger(JwtTokenUtil.class);@Value("${jwt.secret}")private String jwtSecret;@Value("${jwt.expiration}")private long jwtExpiration;// 创建签名密钥private Key getSigningKey() {byte[] keyBytes = jwtSecret.getBytes(StandardCharsets.UTF_8);return new SecretKeySpec(keyBytes, SignatureAlgorithm.HS512.getJcaName());}// 生成JWT令牌public String generateToken(String username) {Date now = new Date();Date expiryDate = new Date(now.getTime() + jwtExpiration);return Jwts.builder().setSubject(username).setIssuedAt(now).setExpiration(expiryDate).signWith(getSigningKey(), SignatureAlgorithm.HS256).compact();}// 从令牌中获取用户名public String getUsernameFromToken(String token) {Claims claims = Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();return claims.getSubject();}// 验证令牌public boolean validateToken(String token, UserDetails userDetails) {String username = getUsernameFromToken(token);return username.equals(userDetails.getUsername()) && !isTokenExpired(token);}
}
2. JWT认证过滤器
JwtAuthenticationFilter是一个拦截器,负责从请求头中提取JWT令牌并验证:
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {@Autowiredprivate JwtTokenUtil jwtTokenUtil;@Autowiredprivate UserDetailsServiceImpl userDetailsService;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {try {// 获取Authorization头String header = request.getHeader("Authorization");String username = null;String jwt = null;// 检查Authorization头格式if (header != null && header.startsWith("Bearer ")) {jwt = header.substring(7);username = jwtTokenUtil.getUsernameFromToken(jwt);}// 验证令牌并设置认证信息if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {UserDetails userDetails = userDetailsService.loadUserByUsername(username);if (jwtTokenUtil.validateToken(jwt, userDetails)) {UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(authentication);}}} catch (Exception e) {logger.error("无法设置用户认证: {}", e);}filterChain.doFilter(request, response);}
}
四、用户认证流程
1. 登录处理
AuthController中的登录方法处理用户认证请求:
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody UserLoginDTO loginDTO) {try {// 1. 创建认证请求对象UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginDTO.getUsername(),loginDTO.getPassword());// 2. 执行身份验证Authentication authentication = authenticationManager.authenticate(authenticationToken);// 3. 设置认证信息到安全上下文SecurityContextHolder.getContext().setAuthentication(authentication);// 4. 生成JWT令牌String token = jwtTokenUtil.generateToken(authentication.getName());// 5. 返回登录成功响应Map<String, Object> successResponse = new HashMap<>();successResponse.put("success", true);successResponse.put("message", "登录成功");successResponse.put("token", token);successResponse.put("username", authentication.getName());return ResponseEntity.ok(successResponse);} catch (BadCredentialsException e) {// 处理密码错误return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用户名或密码错误");} catch (AuthenticationException e) {// 处理其他认证异常return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("身份验证错误");}
}
2. 用户详情加载服务
UserDetailsServiceImpl负责从数据库加载用户信息:
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserRepository userRepository;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 从数据库中查找用户User user = userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + username));// 直接返回User实体,因为它已经实现了UserDetails接口return user;}
}
五、总结
基于JWT的认证方案,结合了Spring Security的强大功能和JWT的灵活特性,实现了一个安全、可扩展的用户认证系统。该方案具有以下优势:
- 无状态性:服务器不需要存储会话信息,便于水平扩展
- 跨域支持:JWT可以在不同域之间安全传输
- 前后端分离友好:适合现代前后端分离的Web应用架构
- 安全性高:通过签名和加密保障令牌安全
