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

4431 字
22 分钟
登录 | 滑块验证码、登录与注册完整流程 | 设计文档

认证模块 (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://tblog.mmzhiku.xyz/posts/projects/projects-auth-flow-zsk-auth/
作者
MmzMing
发布于
2026-05-02
许可协议
CC BY-NC-SA 4.0

评论区

看板娘
公告
友链 互换友链

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

查看详情
维护 服务器升级

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

欢迎 关于我的介绍

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

查看详情
音乐
封面

音乐

暂未播放

0:00
0:00
暂无歌词
标签
# AI 6 # 认证 5 # 安全 4 # 登录 3 # Skill 2 # Redis 2 # Bitmap 2 # 部署 2 # Java 2 # 并发编程 2 # 性能优化 2 # 前端 1 # 博客 1 # Prompt 1 # 工作流 1 # RAG 1 # Cloudflare 1 # 缓存设计 1 # 高性能 1 # Bot 1 # Umami 1 # Vercel 1 # 线程池 1 # 虚拟线程 1 # 分布式 1 # JWT 1 # OAuth2 1 # MinIO 1 # 文件存储 1 # 扫码登录 1 # WebSocket 1 # Agent 1 # Oracle 1 # 数据库 1
目录

隐私政策

更新日期: 2026/5/19
生效日期: 2026/5/19

导言#

MmzMing的知识库 是一款由 MmzMing(以下简称“我们”)提供的产品。您在使用我们的服务时,我们可能会收集和使用您的相关信息。我们希望通过本《隐私政策》向您说明,在使用我们的服务时,我们如何收集、使用、储存和分享这些信息,以及我们为您提供的访问、更新、控制和保护这些信息的方式。

本《隐私政策》与您所使用的 MmzMing的知识库 服务息息相关,希望您仔细阅读,在需要时,按照本《隐私政策》的指引,作出您认为适当的选择。本《隐私政策》中涉及的相关技术词汇,我们尽量以简明扼要的表述,并提供进一步说明的链接,以便您的理解。

您使用或继续使用我们的服务,即意味着同意我们按照本《隐私政策》收集、使用、储存和分享您的相关信息。

如对本《隐私政策》或相关事宜有任何问题,请通过 784774835@qq.com 与我们联系。

1. 我们收集的信息#

我们或我们的第三方合作伙伴提供服务时,可能会收集、储存和使用下列与您有关的信息。如果您不提供相关信息,可能无法注册成为我们的用户或无法享受我们提供的某些服务,或者无法达到相关服务拟达到的效果。

  • 个人信息:您在注册账户或使用我们的服务时,向我们提供的相关个人信息,例如电话号码、电子邮件等。
  • 日志信息:指您使用我们的服务时,系统可能通过 cookies、标识符及相关技术收集的信息,包括您的 设备信息浏览信息点击信息,并将该等信息储存为日志信息,为您提供个性化的用户体验、保障服务安全。您可以通过浏览器设置拒绝或管理 cookie、标识符或相关技术的使用。
  • 位置信息:指您开启设备定位功能并使用我们基于位置提供的相关服务时,收集的有关您位置的信息,包括:
    • 您通过具有定位功能的移动设备使用我们的服务时,通过 GPS 或 WiFi 等方式收集的您的地理位置信息;
    • 您可以通过关闭定位功能,停止对您的地理位置信息的收集。

2. 信息的存储#

2.1 信息存储的方式和期限#

我们会通过安全的方式存储您的信息,包括本地存储(例如利用 APP 进行数据缓存)、数据库和服务器日志。

一般情况下,我们只会在为实现服务目的所必需的时间内或法律法规规定的条件下存储您的个人信息。

2.2 信息存储的地域#

我们会按照法律法规规定,将境内收集的用户个人信息存储于中国境内。

目前我们不会跨境传输或存储您的个人信息。将来如需跨境传输或存储的,我们会向您告知信息出境的目的、接收方、安全保证措施和安全风险,并征得您的同意。

2.3 产品或服务停止运营时的通知#

当我们的产品或服务发生停止运营的情况时,我们将以推送通知、公告等形式通知您,并在合理期限内删除您的个人信息或进行匿名化处理,法律法规另有规定的除外。

3. 信息安全#

我们使用各种安全技术和程序,以防信息的丢失、不当使用、未经授权阅览或披露。例如,在某些服务中,我们将利用加密技术(例如 SSL)来保护您提供的个人信息。但请您理解,由于技术的限制以及可能存在的各种恶意手段,在互联网行业,即便竭尽所能加强安全措施,也不可能始终保证信息百分之百的安全。您需要了解,您接入我们的服务所用的系统和通讯网络,有可能因我们可控范围外的因素而出现问题。

4. 我们如何使用信息#

我们可能将在向您提供服务的过程之中所收集的信息用作下列用途:

  • 向您提供服务;
  • 在我们提供服务时,用于身份验证、客户服务、安全防范、诈骗监测、存档和备份用途,确保我们向您提供的产品和服务的安全性;
  • 帮助我们设计新服务,改善我们现有服务;
  • 使我们更加了解您如何接入和使用我们的服务,从而针对性地回应您的个性化需求,例如语言设定、位置设定、个性化的帮助服务和指示,或对您和其他用户作出其他方面的回应;
  • 向您提供与您更加相关的广告以替代普遍投放的广告;
  • 评估我们服务中的广告和其他促销及推广活动的效果,并加以改善;
  • 软件认证或管理软件升级;
  • 让您参与有关我们产品和服务的调查。

5. 信息共享#

目前,我们不会主动共享或转让您的个人信息至第三方,如存在其他共享或转让您的个人信息或您需要我们将您的个人信息共享或转让至第三方情形时,我们会直接或确认第三方征得您对上述行为的明示同意。

为了投放广告,评估、优化广告投放效果等目的,我们需要向广告主及其代理商等第三方合作伙伴共享您的部分数据,要求其严格遵守我们关于数据隐私保护的措施与要求,包括但不限于根据数据保护协议、承诺书及相关数据处理政策进行处理,避免识别出个人身份,保障隐私安全。

我们不会向合作伙伴分享可用于识别您个人身份的信息(例如您的姓名或电子邮件地址),除非您明确授权。

我们不会对外公开披露所收集的个人信息,如必须公开披露时,我们会向您告知此次公开披露的目的、披露信息的类型及可能涉及的敏感信息,并征得您的明示同意。

随着我们业务的持续发展,我们有可能进行合并、收购、资产转让等交易,我们将告知您相关情形,按照法律法规及不低于本《隐私政策》所要求的标准继续保护或要求新的控制者继续保护您的个人信息。

另外,根据相关法律法规及国家标准,以下情形中,我们可能会共享、转让、公开披露个人信息无需事先征得您的授权同意:

  • 与国家安全、国防安全直接相关的;
  • 与公共安全、公共卫生、重大公共利益直接相关的;
  • 犯罪侦查、起诉、审判和判决执行等直接相关的;
  • 出于维护个人信息主体或其他个人的生命、财产等重大合法权益但又很难得到本人同意的;
  • 个人信息主体自行向社会公众公开个人信息的;
  • 从合法公开披露的信息中收集个人信息的,如合法的新闻报道、政府信息公开等渠道。

6. 您的权利#

在您使用我们的服务期间,我们可能会视产品具体情况为您提供相应的操作设置,以便您可以查询、删除、更正或撤回您的相关个人信息,您可参考相应的具体指引进行操作。此外,我们还设置了投诉举报渠道,您的意见将会得到及时的处理。如果您无法通过上述途径和方式行使您的个人信息主体权利,您可以通过本《隐私政策》中提供的联系方式提出您的请求,我们会按照法律法规的规定予以反馈。

当您决定不再使用我们的产品或服务时,可以申请注销账户。注销账户后,除法律法规另有规定外,我们将删除或匿名化处理您的个人信息。

7. 变更#

我们可能适时修订本《隐私政策》的条款。当变更发生时,我们会在版本更新时向您提示新的《隐私政策》,并向您说明生效日期。请您仔细阅读变更后的《隐私政策》内容,若您继续使用我们的服务,即表示您同意我们按照更新后的《隐私政策》处理您的个人信息。

8. 未成年人保护#

我们鼓励父母或监护人指导未满十八岁的未成年人使用我们的服务。我们建议未成年人鼓励他们的父母或监护人阅读本《隐私政策》,并建议未成年人在提交的个人信息之前寻求父母或监护人的同意和指导。