【Spring Boot 认证登录注册模块全解析】:JWT+BCrypt+Redis 企业级实践
🔥你好我是fengxin_rou这是我的个人主页fengxin_rou的主页
❄️欢迎查看我的专栏我的专栏
《Java后端学习》、《JAVASE基础》、《JUC并发》、《redis》、《JVM虚拟机》、《MYSQL》、《黑马点评》、《rabbitmq》、《JavaWeb+AI的talis学习系统》、《苍穹外卖》
目录
前言
一、模块基础:Java Record 与统一配置中心
1.1 Java Record 简化 DTO 开发
1.2 统一认证配置 AuthProperties
二、安全核心:加密算法与 JWT 令牌实现
2.1 BCrypt 密码加密(企业级标准)
2.2 JWT 令牌签发与验证
2.3 PEM 密钥读取工具
三、权限控制:Spring Security 安全配置
3.1 核心安全配置
3.2 CORS 跨域配置
四、支撑能力:验证码与刷新令牌管理
4.1 Redis 验证码存储与防刷
4.2 Refresh Token 白名单机制
五、业务核心:登录注册完整流程
5.1 标识标准化(关键细节)
5.2 发送验证码
5.3 用户注册
5.4 双模式登录
5.5 令牌刷新
5.6 重置密码
5.7 密码策略校验
5.8 正则格式校验
六、结语
前言
在前后端分离与微服务架构下,认证授权模块是系统安全的核心入口。本文基于 Spring Boot + Spring Security 实现一套完整的登录、注册、验证码、令牌管理方案,采用JWT 无状态认证、BCrypt 密码加密、Redis 存储验证码与刷新令牌,兼顾安全性、易用性与扩展性,可直接用于企业级项目落地。
一、模块基础:Java Record 与统一配置中心
1.1 Java Record 简化 DTO 开发
Java 16 提供的Record是一种极简数据载体类,会自动生成构造器、getter、equals、hashCode、toString,大幅减少样板代码,非常适合定义 DTO、VO 等只读数据对象。
// 用户认证响应 DTO public record AuthUserResponse( Long id, String nickname, String avatar, String phone, String zhId, LocalDate birthday, String school, String bio, String gender, String tagJson ) {}使用示例:
// 直接构建对象 AuthUserResponse user = new AuthUserResponse(123L, "张三", ...); // 自动生成无 get 前缀的 getter Long userId = user.id(); String nick = user.nickname();1.2 统一认证配置 AuthProperties
通过@ConfigurationProperties绑定 YML 配置,统一管理 JWT、验证码、密码策略,支持配置文件覆盖默认值,实现配置与代码解耦。
application.yml 核心配置:
yaml
auth: jwt: issuer: zhiquang # 签发者 key-id: zhiguang-key # 密钥ID private-key: classpath:keys/private.pem public-key: classpath:keys/public.pem access-token-ttl: PT15M # 访问令牌15分钟 refresh-token-ttl: P7D # 刷新令牌7天 verification: code-length: 6 # 验证码长度 ttl: PT5M # 有效期5分钟 max-attempts: 5 # 最大尝试次数 send-interval: PT60S # 发送间隔60秒 daily-limit: 10 # 每日上限10条 password: bcrypt-strength: 12 # 加密强度 min-length: 8 # 最小长度8位配置绑定类:
@Data @ConfigurationProperties(prefix = "auth") public class AuthProperties { private final Jwt jwt = new Jwt(); private final Verification verification = new Verification(); private final Password password = new Password(); @Data public static class Jwt { private String issuer = "zhiquang"; private Duration accessTokenTtl = Duration.ofMinutes(15); private Duration refreshTokenTtl = Duration.ofDays(7); private String keyId = "zhiguang-key"; private Resource privateKey; private Resource publicKey; } }二、安全核心:加密算法与 JWT 令牌实现
2.1 BCrypt 密码加密(企业级标准)
普通哈希(SHA256)速度极快,易被暴力破解与彩虹表攻击。BCrypt自带随机盐、可调整计算成本、故意降低运算速度,是密码存储的行业标准。
@Configuration @EnableConfigurationProperties(AuthProperties.class) @RequiredArgsConstructor public class AuthConfiguration { private final AuthProperties properties; /** * 密码加密器 Bean */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder( properties.getPassword().getBcryptStrength() ); } }使用方式:
// 注册:加密密码 String encoded = passwordEncoder.encode(rawPassword); // 登录:校验密码 boolean match = passwordEncoder.matches(rawPassword, encodedPassword);2.2 JWT 令牌签发与验证
JWT(JSON Web Token)由Header(头部)、Payload(负载)、Signature(签名)三部分组成,采用RSA 非对称加密:私钥签发令牌,公钥验证令牌,防止篡改。
核心 Bean 实现:
/** * JWT 编码器:用于签发令牌 */ @Bean public JwtEncoder jwtEncoder() { AuthProperties.Jwt jwtProps = properties.getJwt(); RSAPrivateKey privateKey = PemUtils.readPrivateKey(jwtProps.getPrivateKey()); RSAPublicKey publicKey = PemUtils.readPublicKey(jwtProps.getPublicKey()); RSAKey rsaKey = new RSAKey.Builder(publicKey) .privateKey(privateKey) .keyID(jwtProps.getKeyId()) .build(); JWKSource<SecurityContext> jwkSource = new ImmutableJWKSet<>(new JWKSet(rsaKey)); return new NimbusJwtEncoder(jwkSource); } /** * JWT 解码器:用于验证令牌 */ @Bean public JwtDecoder jwtDecoder() { RSAPublicKey publicKey = PemUtils.readPublicKey(properties.getJwt().getPublicKey()); return NimbusJwtDecoder.withPublicKey(publicKey).build(); }令牌设计:
- Access Token:有效期 15 分钟,用于接口资源访问
- Refresh Token:有效期 7 天,仅用于刷新 Access Token
- 令牌自带用户信息、过期时间、签发者,服务端无需存储会话
2.3 PEM 密钥读取工具
PemUtils 用于读取 RSA 密钥文件,将 PEM 格式密钥转为 Java 可识别的RSAPrivateKey/RSAPublicKey,为 JWT 签名提供密钥支持。
三、权限控制:Spring Security 安全配置
3.1 核心安全配置
采用无状态 JWT 认证,关闭 CSRF、开启 CORS、禁用 Session,适配前后端分离架构。
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) // 关闭 CSRF .cors(Customizer.withDefaults()) // 开启跨域 // 无状态会话 .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) .authorizeHttpRequests(auth -> auth // 放行认证相关接口 .requestMatchers("/api/v1/auth/**").permitAll() .requestMatchers("/actuator/health", "/api/v1/knowposts/feed").permitAll() .anyRequest().authenticated() ) // 开启 JWT 资源服务校验 .oauth2ResourceServer(oauth -> oauth.jwt(Customizer.withDefaults())); return http.build(); }3.2 CORS 跨域配置
支持前后端分离跨域请求,生产环境需将allowedOrigins设为业务域名白名单。
@Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(List.of("*")); // 生产替换为白名单 config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowedHeaders(List.of("Authorization", "Content-Type")); config.setAllowCredentials(false); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; }四、支撑能力:验证码与刷新令牌管理
4.1 Redis 验证码存储与防刷
使用 Redis Hash 结构存储验证码、尝试次数、最大尝试次数,实现发送频率限制、每日上限、错误次数限制。
// 验证码校验核心逻辑 @Override public VerificationCheckResult verify(String scene, String identifier, String code) { String key = buildKey(scene, identifier); HashOperations<String, String, String> ops = redisTemplate.opsForHash(); Map<String, String> data = ops.entries(key); // 校验验证码是否存在、是否匹配、尝试次数是否超限 }验证码服务流程:
- 校验手机号 / 邮箱格式
- 检查发送间隔与日上限
- 生成 6 位数字验证码
- 存入 Redis 并设置过期时间
- 调用发送器发送短信 / 邮件
4.2 Refresh Token 白名单机制
JWT 无法主动失效,通过Redis 白名单实现刷新令牌的主动撤销、轮换、登出,解决 JWT 安全痛点。
刷新流程:
- Access Token 过期 → 前端携带 Refresh Token 请求
/refresh - 后端校验令牌合法性 + Redis 白名单是否存在
- 验证通过 → 签发新令牌对 + 撤销旧 Refresh Token
- 验证失败 → 返回未登录,强制重新登录
五、业务核心:登录注册完整流程
5.1 标识标准化(关键细节)
统一处理用户输入:手机号去空格、邮箱转小写,避免因格式不一致导致重复注册、查询失败。
// 标识标准化 private String normalizeIdentifier(IdentifierType type, String value) { if (type == PHONE) { return value.trim().replaceAll("\\s+", ""); } else if (type == EMAIL) { return value.trim().toLowerCase(); } return value.trim(); }5.2 发送验证码
按场景做存在性校验:
- 注册场景:账号必须不存在
- 登录 / 重置密码场景:账号必须已存在
5.3 用户注册
public AuthResponse register(RegisterRequest request) { // 1. 校验协议、标识格式、验证码 // 2. 标准化标识,校验唯一性 // 3. 密码策略校验 + BCrypt 加密 // 4. 保存用户信息 // 5. 签发 Access + Refresh 令牌 // 6. 记录登录审计日志 // 7. 返回用户信息 + 令牌 }5.4 双模式登录
支持密码登录+验证码登录:
- 密码登录:匹配加密后哈希值
- 验证码登录:校验 Redis 中验证码
- 登录成功统一签发令牌
5.5 令牌刷新
public TokenResponse refresh(TokenRefreshRequest request) { // 1. 解码 Refresh Token 并校验类型 // 2. 检查 Redis 白名单是否有效 // 3. 签发新令牌对 // 4. 撤销旧 Refresh Token // 5. 存储新令牌到白名单 }5.6 重置密码
验证验证码 → 更新密码 →撤销该用户所有 Refresh Token→ 强制全端重新登录,提升账号安全性。
5.7 密码策略校验
private void validatePassword(String password) { // 非空、长度 ≥8、包含字母+数字 boolean hasLetter = password.chars().anyMatch(Character::isLetter); boolean hasDigit = password.chars().anyMatch(Character::isDigit); if (!hasLetter || !hasDigit) { throw new BusinessException("密码必须包含字母和数字"); } }5.8 正则格式校验
// 手机号正则 private static final Pattern PHONE_PATTERN = Pattern.compile("^1\\d{10}$"); // 邮箱正则 private static final Pattern EMAIL_PATTERN = Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$", Pattern.CASE_INSENSITIVE);六、结语
本文完整实现了企业级 Spring Boot 登录注册认证模块,以 Spring Security 为权限底座、JWT 为无状态令牌、BCrypt 保障密码安全、Redis 支撑高并发验证码与令牌管理,覆盖注册、登录、验证码、令牌刷新、密码重置、安全校验全流程。
该方案具备高安全、无状态、易扩展、易部署特点,适用于电商、社交、管理后台、微服务网关等场景。后续可扩展多因素认证、第三方登录、接口限流、日志审计、异常登录检测等能力,进一步提升系统安全等级。
