当前位置: 首页 > news >正文

【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); // 校验验证码是否存在、是否匹配、尝试次数是否超限 }

验证码服务流程:

  1. 校验手机号 / 邮箱格式
  2. 检查发送间隔与日上限
  3. 生成 6 位数字验证码
  4. 存入 Redis 并设置过期时间
  5. 调用发送器发送短信 / 邮件

4.2 Refresh Token 白名单机制

JWT 无法主动失效,通过Redis 白名单实现刷新令牌的主动撤销、轮换、登出,解决 JWT 安全痛点。

刷新流程:

  1. Access Token 过期 → 前端携带 Refresh Token 请求/refresh
  2. 后端校验令牌合法性 + Redis 白名单是否存在
  3. 验证通过 → 签发新令牌对 + 撤销旧 Refresh Token
  4. 验证失败 → 返回未登录,强制重新登录

五、业务核心:登录注册完整流程

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 支撑高并发验证码与令牌管理,覆盖注册、登录、验证码、令牌刷新、密码重置、安全校验全流程。

该方案具备高安全、无状态、易扩展、易部署特点,适用于电商、社交、管理后台、微服务网关等场景。后续可扩展多因素认证、第三方登录、接口限流、日志审计、异常登录检测等能力,进一步提升系统安全等级。

http://www.cnnetsun.cn/news/2566817.html

相关文章:

  • DELL G3装Ubuntu后WiFi挂了?手把手教你精准查询网卡型号并找对驱动(避坑指南)
  • 告别游戏卡顿!保姆级教程:在Win10上彻底搞定Antimalware Service高占用
  • 趋势科技提醒注意已遭利用的 Apex One 0day 漏洞
  • zotero修改:(1)英文作者三人以上出现“等”
  • 文档格式兼容性挑战与渐进式渲染优化:docxjs库的Web文档渲染架构解析
  • 智能手机多摄像头高光谱成像系统设计与实现
  • 告别外部中断!用EnableInterrupt库轻松搞定Arduino Nano多通道PWM读取(附完整代码)
  • 从频域到时域:聊聊宽带波束形成的两种实现路径与工程选型心得
  • Unity性能适配实战:用SystemInfo判断玩家设备,动态调整画质和特效(附完整代码)
  • Linux下MariaDB 10安装与配置指南
  • 基于OTA芯片的三相正弦波压控振荡器设计与实现
  • 协程详细介绍
  • D37: 周复盘:ToB 项目的 AI 落地方法论
  • 安卓手机安装龙虾openclaw接入deepseek
  • Win10系统清理避坑指南:你的BAT脚本真的安全吗?盘点那些不能乱删的文件
  • 支付宝商户池:收款防风控专属安全通道
  • 一匹来自顺德的布,凭什么走上国际时装周
  • html2pdf-chrome:一个 HTML 转 PDF 的 Go 库 / 服务,依旧是现阶段效果最佳的
  • Unity JSON解析救星:Newtonsoft.Json-for-Unity实战指南
  • C++基础 类和对象(三)
  • 别再折腾驱动了!用DKMS一劳永逸管理你的Linux网卡(以RTL8822CE/Ubuntu 18.04为例)
  • 别再死记硬背了!用Wirtinger导数轻松搞定复数求导(附Python代码验证)
  • 别再傻等自动下载了!手把手教你从国内镜像站搞定Wine 5.0的mono和gecko插件
  • LOOKAHEAD REASONING:大型推理模型的并行加速技术
  • RK3588 Debian 系统安装与WiFi/SSH配置笔记
  • FPG财盛国际:从风险提示看平台责任意识
  • Linux系统启动慢?从UEFI的DXE阶段入手,优化驱动加载让你的开机快人一步
  • 【复现】中国上市公司全要素生产率测算与分析(论文+数据)
  • 从Sora 2原始张量到可交付MP4:端到端Pipeline中被92%开发者忽略的色彩空间转换断点(BT.2020→BT.709→sRGB三级校准手册)
  • 【Claude AI深度SWOT解码】:20年AI架构师亲授,4大维度拆解其商用致命短板与突围路径