登录 | 滑块验证码、登录与注册完整流程

登录 | 滑块验证码、登录与注册完整流程

2026年5月2日星期六·#设计文档认证/登录/安全·4427 字 22 分钟··
简介

zsk-auth 认证模块的登录与注册完整流程,涵盖滑块验证码防刷、RSA 加密传输、BCrypt 哈希存储、邮箱验证码身份核验等安全机制。

认证模块 (zsk-auth) 登录与注册完整流程#

详细代码地址zsk-cloud 主要实现:滑块验证码(防刷) -> RSA 加密传输(防窃听) -> BCrypt 哈希存储(防拖库) -> 邮箱验证码(身份核验)

流程图(不包含异常处理)#

sequenceDiagram participant User as 用户 participant Frontend as 前端 (Vue/React) participant Auth as 认证服务 (zsk-auth) participant Redis as Redis 缓存 participant UserSvc as 用户服务 (zsk-system) Note over User, Auth: 1. 滑块验证码流程 (防刷) User->>Frontend: 打开登录/注册页 Frontend->>Auth: GET /captcha (获取验证码) Auth->>Redis: 存入 X 坐标 (Key: captcha_code:{uuid}) Auth-->>Frontend: 返回背景图、拼图、UUID User->>Frontend: 拖动滑块 Frontend->>Auth: POST /captcha/check (校验验证码) Auth->>Redis: 比对 X 坐标 Redis-->>Auth: 校验通过 Auth->>Redis: 生成 verifyToken (Key: captcha_verified:{token} 带过期时间) Auth-->>Frontend: 返回 verifyToken Note over User, Auth: 2. 邮箱验证码流程 (身份验证) User->>Frontend: 点击发送验证码 Frontend->>Auth: POST /email/code (携带 verifyToken) Auth->>Redis: 校验 verifyToken Auth->>User: 发送邮件验证码 (6位数字) Auth->>Redis: 缓存验证码 (Key: email_code:{email}) Note over User, Auth: 3. 注册/登录提交流程 Frontend->>Auth: GET /public-key (获取 RSA 公钥) Auth-->>Frontend: 返回 RSA 公钥 Frontend->>Frontend: RSA 加密密码 (公钥) User->>Frontend: 填写信息并提交 (含加密密码、验证码) Frontend->>Auth: POST /login 或 /register Auth->>Redis: 校验邮箱验证码 Auth->>Auth: RSA 解密密码 (私钥) alt 注册 (Register) Auth->>Auth: BCrypt 哈希密码 Auth->>UserSvc: 创建新用户 else 登录 (Login) Auth->>UserSvc: 获取用户信息 (含 BCrypt 密码) Auth->>Auth: BCrypt 比对密码 (明文 vs 哈希) Auth ->>Redis:设置JWT过期时间 Auth-->>Frontend: 生成 JWT Token(携带用户信息、权限等) end

流程图 (包含异常处理版)#

sequenceDiagram participant User as 用户 participant Frontend as 前端 (Vue/React) participant Auth as 认证服务 (zsk-auth) participant Redis as Redis 缓存 participant UserSvc as 用户服务 (zsk-system) participant MailSvc as 邮件服务 participant LogSvc as 日志服务 Note over User, Auth: 1. 滑块验证码流程 (防刷+防复用) User->>Frontend: 打开登录/注册页 Frontend->>Auth: GET /captcha (携带timestamp+nonce+签名) Auth->>Auth: 校验请求签名/防重放 alt 签名无效/重放攻击 Auth-->>Frontend: 返回403禁止访问 Auth->>LogSvc: 记录异常请求日志 else 签名有效 Auth->>Auth: 生成滑块验证码(背景图+拼图+X坐标+轨迹阈值) Auth->>Redis: 存入X坐标+轨迹阈值 (Key: captcha_code:{uuid}, Expire: 2min) Auth-->>Frontend: 返回背景图、拼图、UUID User->>Frontend: 拖动滑块(记录滑动轨迹/时长) Frontend->>Auth: POST /captcha/check (UUID+滑动X坐标+轨迹+时长) Auth->>Redis: 读取缓存的X坐标+轨迹阈值 (检查是否过期) alt 验证码过期/不存在 Auth-->>Frontend: 返回验证码过期 Auth->>LogSvc: 记录验证码过期日志 else 校验X坐标/轨迹/时长 alt 校验不通过 Auth-->>Frontend: 返回验证码错误 Auth->>LogSvc: 记录验证码错误日志 else 校验通过 Redis->>Redis: 删除 captcha_code:{uuid} (一次性使用) Auth->>Redis: 生成 verifyToken (Key: captcha_verified:{token}, Expire: 5min) Auth-->>Frontend: 返回 verifyToken end end end Note over User, Auth: 2. 邮箱验证码流程 (防刷+防重复发送) User->>Frontend: 点击发送验证码 Frontend->>Auth: POST /email/code (verifyToken+email+timestamp+签名) Auth->>Redis: 校验 verifyToken (是否存在/过期) alt verifyToken无效/过期 Auth-->>Frontend: 返回验证失效,请重新验证滑块 else verifyToken有效 Auth->>Redis: 检查 email_code:{email}_lock (是否在60秒冷却中) alt 冷却中 Auth-->>Frontend: 返回验证码发送频繁,请稍后再试 else 可发送 Auth->>Redis: 设置 email_code:{email}_lock (Expire: 60s) Auth->>Auth: 生成6位邮箱验证码 Auth->>Redis: 缓存验证码 (Key: email_code:{email}, Expire: 5min) Auth->>MailSvc: 发送邮件验证码 alt 邮件发送失败 Auth->>Redis: 删除 email_code:{email}_lock Auth-->>Frontend: 返回验证码发送失败,请重试 Auth->>LogSvc: 记录邮件发送失败日志 else 发送成功 Auth-->>Frontend: 返回发送成功(隐藏验证码) Auth->>LogSvc: 记录验证码发送成功日志 end end end Note over User, Auth: 3. 注册/登录提交流程 (加密+权限+刷新Token) Frontend->>Auth: GET /public-key (获取RSA公钥) Auth-->>Frontend: 返回RSA公钥 User->>Frontend: 填写信息(账号/密码/邮箱/验证码) Frontend->>Frontend: RSA加密密码(公钥) Frontend->>Auth: POST /login 或 /register (加密密码+邮箱验证码+签名) Auth->>Redis: 校验邮箱验证码 (检查是否过期/匹配) alt 邮箱验证码错误/过期 Auth-->>Frontend: 返回验证码错误/过期 Auth->>LogSvc: 记录验证码校验失败日志 else 验证码有效 Redis->>Redis: 删除 email_code:{email} (一次性使用) Auth->>Auth: RSA解密密码(私钥) alt 注册 (Register) Auth->>UserSvc: 检查用户名/邮箱是否存在 (超时重试+降级) alt 用户已存在 Auth-->>Frontend: 返回用户已存在 else 用户不存在 Auth->>Auth: BCrypt哈希密码 Auth->>UserSvc: 创建新用户(含基础信息/角色) alt 创建失败 Auth-->>Frontend: 返回注册失败,请重试 Auth->>LogSvc: 记录用户创建失败日志 else 创建成功 Auth->>Auth: 生成JWT Token + RefreshToken Auth->>Redis: 缓存RefreshToken (Key: refresh_token:{token}, Expire: 7天) Auth-->>Frontend: 返回JWT Token (含用户ID/角色) + RefreshToken Auth->>LogSvc: 记录注册成功日志 end end else 登录 (Login) Auth->>UserSvc: 获取用户信息(含BCrypt密码/角色)(超时重试+降级) alt 用户不存在 Auth-->>Frontend: 返回用户不存在 else 用户存在 Auth->>Auth: BCrypt比对密码(明文vs哈希) alt 密码不匹配 Auth-->>Frontend: 返回密码错误 Auth->>LogSvc: 记录登录失败日志 else 密码匹配 Auth->>Auth: 生成JWT Token (含用户ID/角色,Expire: 2h) + RefreshToken (Expire: 7天) Auth->>Redis: 缓存RefreshToken (Key: refresh_token:{token}) Auth-->>Frontend: 返回JWT Token + RefreshToken Auth->>LogSvc: 记录登录成功日志 end end end end

技术栈 (Tech Stack)#

本模块基于 Spring Cloud Alibaba 微服务架构,核心技术组件如下:

分类组件说明
核心框架Spring Boot, Spring Cloud Alibaba微服务基础架构
注册配置Nacos服务注册发现与分布式配置中心
熔断限流Sentinel接口限流(如验证码接口防刷)、熔断降级
安全框架Spring Security认证授权核心框架
令牌管理JWT (JSON Web Token)无状态身份验证令牌
持久化/缓存Redis缓存验证码 (TTL)、Token 黑名单、分布式锁
工具库Hutool, Lombok验证码生成、工具类简化、代码简化
邮件服务Apache Commons Email邮件发送服务(支持 HTML 模板)
加密算法胡图工具的RSA + BCrypt双重加密机制(传输层 RSA,存储层 BCrypt)
<dependencies>
<!-- 本次不包含第三方认证
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
-->
<!-- Spring Security 我这里主要是用到了security的加密服务,security我是全面放行,security用于管认证,而gateway管登录 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Spring Cloud LoadBalancer 搭配security存储用户信息到线程上 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- Sentinel Core -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- swagger -->
<!-- ZSK API System -->
<!-- 远程调用 System微服务,获取用户信息-->
<!-- Nacos Discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Hutool 内包含 BCrypt -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!-- Hutool 生成滑块验证码图片参数-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-captcha</artifactId>
</dependency>
<!-- Apache Commons Email -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
</dependency>
</dependencies>

1. 核心类说明#

类名路径说明
AuthController./auth.controller.AuthController认证接口入口,处理 HTTP 请求
AuthServiceImpl./auth.service.impl.AuthServiceImpl认证业务逻辑实现的核心类
CaptchaServiceImpl./auth.service.impl.CaptchaServiceImpl滑块验证码生成与校验服务
EmailServiceImpl./auth.service.impl.EmailServiceImpl邮件发送与验证码校验服务
EncryptServiceImpl./auth.service.impl.EncryptServiceImplRSA 加解密服务
SecurityUtils./common.security.utils.SecurityUtils密码哈希与比对工具类
LoginRequest./auth.domain.LoginRequest登录请求参数封装
RegisterBody./auth.domain.RegisterBody注册请求参数封装

2. 滑块验证码流程 (Slider Captcha)#

2.1 流程概述#

滑块验证码用于人机识别,防止恶意刷接口。

  1. 生成验证码 (GET /captcha):
    • 生成随机背景图和拼图块。
    • 随机计算缺口位置 (x, y)。
    • 将 x 坐标存入 Redis (Key: captcha_code:{uuid}),有效期 1 分钟。
    • 返回 Base64 格式的图片数据和 uuid。
  2. 校验验证码 (POST /captcha/check):
    • 接收前端上传的 uuid 和滑块移动距离 (code)。
    • 从 Redis 取出 x 坐标比对(允许 ±5 像素误差)。
    • 验证通过后,生成 verifyToken 存入 Redis (Key: captcha_verified:{token}),有效期 5 分钟。
    • 返回 verifyToken,后续发送短信/邮件验证码时需携带此 Token。

2.2 核心代码#

响应对象:CaptchaResponse.java

/**
* 验证码响应对象
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CaptchaResponse {
/** 验证码唯一标识 */
private String uuid;
/** 背景图片(Base64) */
private String bgUrl;
/** 拼图图片(Base64) */
private String puzzleUrl;
/** 滑块Y轴坐标 */
private Integer y;
}

生成验证码:CaptchaServiceImpl.java

@Override
public CaptchaResponse generateSlideCaptcha() {
// ... 生成图片逻辑 ...
// 6. 缓存X坐标用于校验
String uuid = UUID.randomUUID().toString().replace("-", "");
String captchaKey = CacheConstants.CACHE_CAPTCHA_CODE + uuid;
redisService.setCacheObject(captchaKey, String.valueOf(x), 1, TimeUnit.MINUTES);
return CaptchaResponse.builder()
.uuid(uuid)
.bgUrl(bgBase64)
.puzzleUrl(puzzleBase64)
.y(y)
.build();
}

校验验证码:CaptchaServiceImpl.java

@Override
public String validateCaptcha(String uuid, String code) {
String captchaKey = CacheConstants.CACHE_CAPTCHA_CODE + uuid;
String cachedX = redisService.getCacheObject(captchaKey);
// ... 校验逻辑 ...
int x = Integer.parseInt(code);
int targetX = Integer.parseInt(cachedX);
// 允许误差范围 ±5 像素
if (Math.abs(x - targetX) > 5) {
throw new AuthException("验证码错误");
}
// 生成验证通过凭证
String verifyToken = UUID.randomUUID().toString().replace("-", "");
String verifyKey = CacheConstants.CACHE_CAPTCHA_VERIFIED + verifyToken;
redisService.setCacheObject(verifyKey, "true", 5, TimeUnit.MINUTES);
return verifyToken;
}

3. 邮箱验证码流程 (Email Verification)#

3.1 流程概述#

用于注册、登录或找回密码时的身份验证。

  1. 发送验证码 (POST /email/code):
    • 前置校验:必须携带 captchaVerification (滑块验证通过凭证)。
    • 凭证检查:验证 captchaVerification 是否有效(防止跳过人机验证直接刷短信接口)。
    • 生成发送:生成 6 位随机数字,存入 Redis (Key: email_code:{email}),发送 HTML 邮件。
    1. 业务校验 (内部调用):
    • 在注册或登录接口中,调用 emailService.validateEmailCode 校验用户输入的验证码是否匹配且未过期。

3.2 核心代码#

发送入口:AuthController.java

@PostMapping("/email/code")
// 限制每个邮箱每1分钟最多发送5次验证码,需要配置sentinel
@RateLimit(resource = "auth:email:code",key = "#email", count = 5, timeUnit = TimeUnit.MINUTES)
public R<Void> sendEmailCode(@RequestParam String email, @RequestParam String captchaVerification) {
// 验证滑块验证码凭证 (关键安全步骤)
captchaService.verifyCaptchaToken(captchaVerification);
emailService.sendEmailCode(email);
return R.ok();
}

发送逻辑:EmailServiceImpl.java

@Override
public void sendEmailCode(String email) {
String code = generateEmailCode(); // 生成6位数字
String emailKey = CacheConstants.CACHE_EMAIL_CODE + email;
// 缓存验证码,设置过期时间
redisService.setCacheObject(emailKey, code, emailCodeExpire, TimeUnit.SECONDS);
// 发送 HTML 邮件
HtmlEmail htmlEmail = new HtmlEmail();
// ... 配置邮件参数 ...
htmlEmail.setHtmlMsg(getHtmlTemplate(code));
htmlEmail.send();
}

校验逻辑:EmailServiceImpl.java

@Override
public void validateEmailCode(String email, String code) {
if (StringUtils.isEmpty(email) || StringUtils.isEmpty(code)) {
throw new AuthException("邮箱和验证码不能为空");
}
String emailKey = CacheConstants.CACHE_EMAIL_CODE + email;
String cachedCode = redisService.getCacheObject(emailKey);
if (StringUtils.isEmpty(cachedCode)) {
throw new AuthException("验证码已过期");
}
if (!code.equals(cachedCode)) {
throw new AuthException("验证码错误");
}
// 验证通过后删除缓存,防止重复使用
redisService.deleteObject(emailKey);
}

4. 注册流程 (Register)#

4.1 流程概述#

  1. 接收请求AuthController 接收 POST /register 请求。
  2. 参数校验:Spring Validation 校验基础参数格式(非空、长度、邮箱格式等)。
  3. 业务处理 (AuthServiceImpl.register):
    • 密码解密:使用 EncryptService 解密前端传输的 RSA 加密密码。
    • 验证码校验:调用 EmailService 验证邮箱验证码是否正确。
    • 密码规则校验:检查密码长度、确认密码是否一致。
    • 唯一性检查:调用远程用户服务 (RemoteUserService) 检查用户名是否已存在。
    • 密码哈希:使用 SecurityUtils.encryptPassword (BCrypt) 对密码进行哈希处理。
    • 创建用户:构建 SysUserApi 对象,调用远程服务创建新用户。

4.2 核心代码#

请求对象:RegisterBody.java

/**
* 用户注册请求对象
*/
@Data
public class RegisterBody implements Serializable {
/** 用户名 */
@NotBlank(message = "用户名不能为空")
@Length(min = 2, max = 20, message = "用户名长度必须在2到20个字符之间")
private String username;
/** 邮箱 */
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
/** 用户密码 */
@NotBlank(message = "密码不能为空")
private String password;
/** 确认密码 */
@NotBlank(message = "确认密码不能为空")
private String confirmPassword;
/** 验证码内容 */
@NotBlank(message = "验证码不能为空")
private String code;
/** 验证码标识 */
@NotBlank(message = "验证码标识不能为空")
private String uuid;
}

入口:AuthController.java

@Operation(summary = "用户注册")
@PostMapping("/register")
// 限制每个邮箱每1分钟最多注册10次,需要配置sentinel
@RateLimit(resource = "auth:register", key = "#registerBody.email", count = 10, timeUnit = TimeUnit.MINUTES)
public R<Void> register(@RequestBody @Valid RegisterBody registerBody) {
authService.register(registerBody);
return R.ok();
}

业务逻辑:AuthServiceImpl.java

@Override
public void register(RegisterBody registerBody) {
String username = registerBody.getUsername();
// 1. RSA 密码解密
String password = encryptService.decrypt(registerBody.getPassword());
String confirmPassword = encryptService.decrypt(registerBody.getConfirmPassword());
String code = registerBody.getCode();
String email = registerBody.getEmail();
// 2. 验证邮箱验证码
emailService.validateEmailCode(email, code);
// ... 密码规则校验 ...
// 3. 检查用户是否已存在
R<LoginUser> result = remoteUserService.getUserInfo(username, CommonConstants.REQUEST_SOURCE_INNER);
if (result != null && result.isSuccess() && result.getData() != null) {
throw new BusinessException("保存用户'" + username + "'失败,注册账号已存在");
}
// 4. 构建用户对象并创建
SysUserApi sysUser = new SysUserApi();
sysUser.setUserName(username);
// ... 设置属性 ...
// 5. BCrypt 密码哈希加密
sysUser.setPassword(SecurityUtils.encryptPassword(password));
R<Boolean> registerResult = remoteUserService.createUser(sysUser);
// ... 结果处理 ...
}

5. 登录流程 (Login)#

5.1 流程概述#

  1. 接收请求AuthController 接收 POST /login 请求,并通过 @RateLimit 进行限流。
  2. 分发逻辑AuthServiceImpl 根据 loginType (password/email/third-party) 分发到不同的处理方法。
  3. 密码登录 (passwordLogin)
    • 获取用户信息:调用 RemoteUserService 根据用户名获取用户信息。
    • 验证码校验:校验邮箱验证码。
    • 密码解密:使用 EncryptService 解密前端传输的 RSA 加密密码。
    • 密码比对:使用 SecurityUtils.matchesPassword (BCrypt) 比对解密后的密码与数据库中的哈希密码。
    • 状态检查:检查账号是否被停用。
    • 生成令牌:生成 JWT Token 返回。

5.2 核心代码#

请求对象:RegisterBody.java

@Data
public class LoginRequest {
/**
* 用户名
*/
@Length(min = 2, max = 20, message = "用户名长度必须在2到20个字符之间")
private String username;
/**
* 密码
*/
@NotBlank(message = "密码不能为空")
private String password;
/**
* 邮箱验证码
*/
@NotBlank(message = "验证码不能为空")
private String code;
/**
* 登录类型(password-密码登录,email-邮箱登录,qq-QQ登录,wechat-微信登录,github-GitHub登录)
*/
@NotBlank(message = "登录类型不能为空")
private String loginType;
/**
* 邮箱地址(邮箱登录时必填)
*/
@Email(message = "邮箱格式不正确")
private String email;
/**
* 邮箱验证码(邮箱登录时必填)
*/
@NotBlank(message = "邮箱验证码不能为空")
private String emailCode;
// 第三方授权码(省略掉)
}

发送入口:login.java

/**
* 用户登录
*
* @param request 登录参数
* @return 登录结果
*/
@Operation(summary = "用户登录")
@PostMapping("/login")
// 限制每个用户每1分钟最多注册10次,需要配置sentinel
@RateLimit(resource = "auth:login", key = "#request.username", count = 10, timeUnit = TimeUnit.MINUTES)
public R<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
LoginResponse response = authService.login(request);
return R.ok(response);
}

业务分发:AuthServiceImpl.java

@Override
public LoginResponse login(LoginRequest request) {
String loginType = request.getLoginType();
return switch (loginType) {
case "password" -> passwordLogin(request);
case "email" -> emailLogin(request);
// ...
default -> throw new AuthException("不支持的登录类型: " + loginType);
};
}

密码登录逻辑:passwordLogin

private LoginResponse passwordLogin(LoginRequest request) {
// ... 参数获取 ...
// 1. 远程调用获取用户信息
R<LoginUser> userResult = remoteUserService.getUserInfo(username, CommonConstants.REQUEST_SOURCE_INNER);
if (userResult == null || !userResult.isSuccess()) {
throw new AuthException("用户不存在");
}
LoginUser loginUser = userResult.getData();
// ... 邮箱验证码校验 ...
// 2. RSA 密码解密
String decryptedPassword = encryptService.decrypt(password);
// 3. BCrypt 密码比对
SysUserApi user = loginUser.getSysUser();
if (!SecurityUtils.matchesPassword(decryptedPassword, user.getPassword())) {
throw new AuthException("用户名或密码错误");
}
// ... 状态检查与 Token 生成 ...
return generateToken(loginUser);
}

6. 安全加密机制 (Security & Encryption)#

本系统采用 RSA + BCrypt 双重加密机制,确保用户密码在传输和存储过程中的安全性。

6.1 传输层加密 (RSA)#

前端在发送密码前,先获取服务端下发的 RSA 公钥进行加密,后端使用私钥解密。这防止了密码在网络传输中被明文截获。

  • 前端:调用 /public-key 获取公钥 -> 使用 JSEncrypt 等库加密密码。
  • 后端EncryptServiceImpl.decrypt 使用私钥解密。

后端 RSA 解密核心代码:

EncryptServiceImpl.java
@Override
public String decrypt(String encryptedData) {
try {
PrivateKey privateKey = getPrivateKey();
// 指定 RSA-OAEP 算法,匹配前端的 SHA-256 哈希
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
// 配置 OAEP 参数
OAEPParameterSpec oaepParams = new OAEPParameterSpec(
"SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT
);
cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
return new String(decryptedBytes);
} catch (Exception e) {
throw new BusinessException("数据解密失败");
}
}

6.2 存储层加密 (BCrypt)#

解密后的原始密码绝不直接存储到数据库。系统使用 BCrypt 算法对密码进行哈希处理。BCrypt 自动加盐,即使相同的密码每次生成的哈希值也不同。

  • 注册/重置密码:使用 SecurityUtils.encryptPassword 生成哈希值存入数据库。
  • 登录验证:使用 SecurityUtils.matchesPassword 验证明文密码是否与哈希值匹配。

后端 BCrypt 工具类代码:

SecurityUtils.java
public class SecurityUtils {
/**
* 生成 BCrypt 密码哈希
* @param password 明文密码
* @return 加密字符串 (含盐)
*/
public static String encryptPassword(String password) {
return BCrypt.hashpw(password);
}
/**
* 验证密码
* @param rawPassword 明文密码
* @param encodedPassword 数据库存储的哈希密码
* @return 是否匹配
*/
public static boolean matchesPassword(String rawPassword, String encodedPassword) {
return BCrypt.checkpw(rawPassword, encodedPassword);
}
}

7. 限流机制 (Rate Limiting)#

本系统实现了多维度的流量控制机制,结合 SentinelRedis 满足不同场景的需求。

7.1 策略概述#

  1. 接口全局限流 (Sentinel): 适用于对某个接口的总并发量或 QPS 进行限制,保护系统不过载。
  2. 业务维度限流 (Redis): 适用于针对特定用户、IP 或业务 ID 的频率限制(例如:限制某用户每分钟只能尝试登录 10 次)。

7.2 部分核心代码#

限流注解:RateLimit.java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
/** 资源名称 (Sentinel用) */
String resource() default "";
/** 业务Key (Redis用,支持SpEL表达式,如 "#user.id") */
String key() default "";
/** 限流提示信息 */
String message() default "请求过于频繁,请稍后再试";
/** 限流阈值 */
double count() default 5;
/** 限流时间单位 */
TimeUnit timeUnit() default TimeUnit.SECONDS;
}

切面逻辑:SentinelAspect.java

@Around("@annotation(rateLimit)")
public Object aroundRateLimit(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable {
String key = rateLimit.key();
// 策略选择:配置了业务Key且Redis可用 -> Redis限流;否则 -> Sentinel限流
if (StringUtils.isNotBlank(key) && redisService != null) {
return handleRedisRateLimit(point, rateLimit);
}
return handleSentinelRateLimit(point, rateLimit);
}
// Redis 分布式限流实现
private Object handleRedisRateLimit(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable {
String resourceName = getResourceName(point, rateLimit.resource());
// 解析 SpEL 表达式获取业务值 (如用户名)
String businessKey = parseSpel(rateLimit.key(), point);
// 生成 Redis Key: rate_limit:auth:login:zhangsan
String redisKey = "rate_limit:" + resourceName + ":" + businessKey;
// 原子递增并校验
Long count = redisService.increment(redisKey, 1);
if (count != null && count == 1) {
// 首次访问设置过期时间
redisService.expire(redisKey, rateLimit.timeUnit().toSeconds(1), TimeUnit.SECONDS);
}
if (count != null && count > rateLimit.count()) {
throw new RateLimitException(rateLimit.message());
}
return point.proceed();
}

使用示例:AuthController.java

/**
* 用户登录
* 限制每个用户名每 10 分钟只能尝试登录 10 次
*/
@PostMapping("/login")
@RateLimit(resource = "auth:login", count = 10, timeUnit = TimeUnit.MINUTES, key = "#request.username")
public R<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
// ...
}
登录 | 滑块验证码、登录与注册完整流程
https://blog1.z2m.store/posts/projects/projects-auth-flow-zsk-auth/
作者
深漂小鱼
发布于
2026-05-02
许可协议
CC BY-NC-SA 4.0

评论区

看板娘
公告
友链互换友链

正在招募技术类博客友链,要求原创、稳定更新。点击了解更多。

查看详情
维护服务器升级

本周日凌晨 2:00-4:00 进行服务器维护,期间站点可能短暂无法访问。

欢迎关于我的介绍

欢迎来到我的博客,我是深耕java、python和react技术开发。热爱技术、持续学习,欢迎同好交流探讨,也欢迎大佬互换友链。

查看详情
音乐
封面

音乐

暂未播放

0:00
0:00
暂无歌词
标签
# 认证2# Umami1# 部署1# Vercel1# 登录1# 安全1# 扫码登录1# WebSocket1
目录
工具

隐私政策

更新日期: 2026 年 7 月 15 日
生效日期: 2026 年 7 月 15 日

适用范围#

本政策适用于 MmzMing 的博客(以下简称“本站”)。本站是个人博客,用于发布和分享内容;不提供账户注册、支付、定位或广告投放服务。访问本站、发表文章评论或在留言板留言前,请阅读本政策。

信息收集与使用#

本站只在提供内容、评论和留言功能,以及维护站点安全所需的范围内处理信息。

  • 访问与统计信息:访问页面时,统计服务可能处理访问时间、页面地址、来源页、浏览器和设备相关信息,用于了解内容访问情况、排查故障和改进站点。
  • 评论信息:使用文章评论功能时,Waline 可能处理您主动提交的昵称、邮箱、站点链接和评论内容;还可能处理 IP 地址等必要信息,用于防止垃圾评论、滥用和维护服务安全。评论内容、昵称和站点链接(如填写)可能公开展示在文章下方;邮箱不会公开展示。
  • 留言信息:留言板使用 Waline /guestbook/ 频道。Waline 可能处理您主动提交的昵称、可选邮箱、站点链接、留言内容和图片,以及浏览器、操作系统、IP 地址等必要的反滥用信息。默认情况下,留言图片以内嵌数据随留言提交;如站点维护者配置了远程图片上传接口,图片会先发送至该接口并在留言中保存返回的图片地址。留言内容、昵称、图片和站点链接(如填写)可能公开展示;邮箱和 IP 地址不会在留言板公开展示。
  • AI 对话信息:使用 AI 搜索时,本站会处理您提交的问题以及最近 6 条对话历史,用于检索博客内容并生成回答。问题和对话历史会发送至 ModelScope,或在第三方接口不可用时由 Cloudflare Workers AI 处理。请勿在 AI 对话中提交密码、Token、身份证件、联系方式或其他敏感信息。

请不要在评论或留言中提交身份证件、银行卡、住址、密码或其他不必要的敏感个人信息。

第三方服务#

为实现本站功能,以下第三方会在各自服务范围内处理相关数据:

  • Umami:用于匿名化的网站访问统计和出站链接点击统计,帮助我了解本站的使用情况。
  • Waline:用于文章评论、留言板及访问量统计。服务会按照其自身规则处理您在评论或留言时提交的信息及必要的反滥用信息。
  • Cloudflare:为本站提供静态资源分发、AI Worker、Vectorize 和相关基础设施。留言板不使用项目 Worker 或 KV 存储。
  • ModelScope:为 AI 搜索提供文本向量和对话模型服务,会处理您提交的问题及发送给模型的最近对话历史。
  • Cloudflare Workers AI:在未配置第三方 AI 接口时提供文本向量和对话模型服务,并处理相同的 AI 请求数据。
  • unpkg:用于加载 Waline 的前端脚本、样式和表情资源;请求这些资源时,您的浏览器会与该服务建立连接。

第三方服务可能有独立的隐私政策和数据保存规则。请在使用相关功能前查阅其规则;本站无法控制其独立的数据处理活动。

本站主要使用浏览器本地存储(Local Storage 或 Session Storage)保存使用偏好,例如主题颜色、明暗模式、文章列表视图和音乐播放设置。留言板会在本地保存匿名资料、未发送草稿和登录状态,以便恢复输入与会话;管理员登录状态仅保存在当前会话。AI 搜索的会话标题和完整对话也会保存在当前浏览器的 Local Storage 中,最长保存 7 天;您可以在 AI 面板中使用“清空全部会话”立即删除这些数据。

本站不主动设置用于广告定向的第一方 Cookie。评论、统计或资源服务可能按照其自身规则使用 Cookie 或类似技术。您可以通过浏览器设置查看、删除或限制 Cookie 和本地存储;清除后,部分偏好或互动状态可能会恢复为默认值,评论功能也可能受到影响。

信息公开、保存与安全#

评论和留言属于公开互动内容,提交后可能被搜索引擎收录、被他人引用或在缓存中短暂保留。请谨慎决定发布内容。除非您提出删除请求、内容违反规则或法律法规另有要求,公开内容会持续保留以维持讨论上下文。

本站会采取合理措施保护数据安全,包括使用 HTTPS、输入校验、内容转义和访问频率限制。但互联网传输和第三方服务均无法保证绝对安全,请理解并自行承担公开发布信息的相应风险。

你的权利#

你可以通过 784774835@qq.com 联系我,申请查询、更正或删除由本站直接保存的评论、留言或相关公开内容。为保护他人权益,请在请求中提供足以定位内容的信息,并说明你与该内容的关系;必要时可能需要进行合理核验。

对于由 Waline、Umami、Cloudflare 或 unpkg 独立处理的数据,你也可以直接向对应服务提供方行使相关权利。删除公开评论或留言后,第三方缓存、搜索引擎索引或他人转载的副本可能无法立即同步删除。

未成年人条款#

未满 14 周岁的未成年人应在监护人同意和指导下使用本站的评论、留言等互动功能。监护人如发现未成年人未经同意提交了个人信息,可通过上述联系方式与我联系,我会在合理范围内协助处理。

政策更新与联系#

我可能因本站功能或适用规则变化更新本政策,更新后的版本将在本站公布并标明日期。继续使用相关功能即表示你已阅读并理解更新后的政策。

如对本政策或数据处理有疑问,请联系 784774835@qq.com

用户协议

更新日期: 2026 年 5 月 19 日
生效日期: 2026 年 5 月 19 日

适用范围#

本协议适用于你访问 MmzMing 的博客,以及使用文章评论、留言板等互动功能的行为。继续浏览本站或提交评论、留言,即表示你已阅读、理解并同意遵守本协议及本站的隐私政策。

评论及留言规则#

请在交流中保持友善、理性和尊重。你不得利用本站发布、传播或实施以下行为:

  • 发布任何违反中华人民共和国法律法规的内容。
  • 发布任何侵犯他人合法权益的内容,包括但不限于隐私、名誉、肖像、著作权、商标权和其他知识产权。
  • 恶意攻击、辱骂、骚扰、威胁、歧视其他用户或任何第三方。
  • 发布垃圾广告、恶意推广、刷屏、灌水,或与讨论主题明显无关的重复内容。
  • 利用本站进行网络诈骗、钓鱼、传播恶意软件,或发布可能危害网络和信息安全的内容。
  • 绕越或试图绕越本站的审核、限流、封禁等管理措施。
  • 冒充他人、伪造身份,或收集、公开他人的个人信息。

内容与访问管理#

你应对自己发布的评论和留言负责,并保证拥有发布该等内容所需的合法权利。论坛管理员有权在不另行通知的情况下删除违规内容、限制或封禁违规账号,或限制其继续使用本站互动功能。

如发现涉嫌违法犯罪、严重侵权或危及本站安全的内容,本站可保留相关记录,并在法律法规要求或必要时向有关部门提供协助。对管理措施有疑问时,可通过文末联系方式说明情况;本站会结合实际情况处理,但不承诺恢复已删除内容或访问权限。

知识产权与内容授权#

本站原创文章、页面设计和其他受保护内容的权利归作者或权利人所有。未经授权,请勿复制、转载、镜像或用于商业用途;法律法规允许的合理使用除外。

你发布评论或留言时,授予本站为展示、存储、备份、审核、删除和维护互动功能所必需的非独占、免费的使用许可。该许可不改变你对原创内容依法享有的权利。

免责声明#

本站内容仅用于个人记录、学习交流和一般信息参考,不构成任何专业意见、承诺或担保。你应结合自身情况独立判断,并对据此采取的行动负责。

评论、留言和外部链接中的内容由其发布者或运营者负责,不代表本站立场。本站会在合理范围内处理明显违规内容,但不保证所有内容均及时发现,也不对第三方网站的可用性、内容、安全性或隐私实践承担责任。

因网络故障、不可抗力、第三方服务异常、维护升级或超出合理控制范围的原因导致本站暂时无法访问、内容延迟或数据丢失的,本站会尽力恢复,但不承担由此产生的间接损失。

未成年人条款#

未满 14 周岁的未成年人应在监护人同意和指导下使用评论、留言等互动功能。监护人应协助未成年人理解本协议,并对其使用行为进行必要的引导。

其他条款#

我可以根据本站功能、管理需要或法律法规变化更新本协议,更新后的版本将在本站公布并标明日期。继续使用本站即视为接受更新后的协议。

本协议的订立、执行和解释适用中华人民共和国法律。因本协议或使用本站产生争议时,双方应先友好协商;协商不成的,依法向有管辖权的人民法院解决。

如对本协议或内容管理有疑问,请联系 784774835@qq.com