验证码修改

This commit is contained in:
2025-12-16 18:26:52 +08:00
parent 62e6365d46
commit f7b0dcd120
10 changed files with 70 additions and 46 deletions

View File

@@ -128,7 +128,8 @@ public class AuthController {
/** /**
* @description 发送邮箱验证码 * @description 发送邮箱验证码
* @param requestBody 包含email字段的请求体 * @param requestBody 包含email和scene字段的请求体
* scene: 业务场景login-登录, register-注册, reset-找回密码, bind-绑定邮箱等)
* @return ResultDomain<Boolean> 发送结果 * @return ResultDomain<Boolean> 发送结果
* @author yslg * @author yslg
* @since 2025-11-03 * @since 2025-11-03
@@ -138,6 +139,7 @@ public class AuthController {
ResultDomain<Map<String, String>> result = new ResultDomain<>(); ResultDomain<Map<String, String>> result = new ResultDomain<>();
String email = requestBody.get("email"); String email = requestBody.get("email");
String scene = requestBody.get("scene");
// 验证邮箱格式 // 验证邮箱格式
if (email == null || email.trim().isEmpty()) { if (email == null || email.trim().isEmpty()) {
@@ -151,10 +153,15 @@ public class AuthController {
return result; return result;
} }
// 验证场景参数
if (scene == null || scene.trim().isEmpty()) {
scene = "default";
}
// 检查是否频繁发送60秒内只能发送一次 // 检查是否频繁发送60秒内只能发送一次
String rateLimitKey = "email:code:ratelimit:" + email; String rateLimitKey = "email:code:ratelimit:" + scene + ":" + email;
if (redisService.hasKey(rateLimitKey)) { if (redisService.hasKey(rateLimitKey)) {
result.fail("验证码已发送,请勿重复发送"); result.fail("验证码已发送,请60秒后再试");
return result; return result;
} }
@@ -168,21 +175,25 @@ public class AuthController {
boolean success = emailUtils.sendVerificationCode(email, code); boolean success = emailUtils.sendVerificationCode(email, code);
if (success) { if (success) {
// 将验证码存储到Redis绑定sessionId有效期5分钟 // 将验证码存储到Redis绑定sessionId和业务场景有效期5分钟
String codeKey = "email:code:" + sessionId; String codeKey = "email:code:" + scene + ":" + sessionId;
String codeValue = email + ":" + code; // 格式:邮箱:验证码 String codeValue = email + ":" + code;
redisService.set(codeKey, codeValue, 5, TimeUnit.MINUTES); redisService.set(codeKey, codeValue, 5, TimeUnit.MINUTES);
// 设置5分钟的发送频率限制 // 设置60秒的发送频率限制
redisService.set(rateLimitKey, "1", 5, TimeUnit.MINUTES); redisService.set(rateLimitKey, "1", 60, TimeUnit.SECONDS);
// 返回sessionId给前端 // 计算过期时间戳(当前时间 + 5分钟
long expireTime = System.currentTimeMillis() + (5 * 60 * 1000);
// 返回sessionId和过期时间给前端
Map<String, String> data = Map.of( Map<String, String> data = Map.of(
"sessionId", sessionId, "sessionId", sessionId,
"expireTime", String.valueOf(expireTime),
"message", "验证码已发送到邮箱" "message", "验证码已发送到邮箱"
); );
logger.info("邮箱验证码已发送,邮箱: {}, sessionId: {}", email, sessionId); logger.info("邮箱验证码已发送,邮箱: {}, scene: {}, sessionId: {}, expireTime: {}", email, scene, sessionId, expireTime);
result.success("验证码已发送到邮箱", data); result.success("验证码已发送到邮箱", data);
} else { } else {
result.fail("验证码发送失败,请稍后重试"); result.fail("验证码发送失败,请稍后重试");
@@ -193,7 +204,8 @@ public class AuthController {
/** /**
* @description 发送手机验证码 * @description 发送手机验证码
* @param requestBody 包含phone字段的请求体 * @param requestBody 包含phone和scene字段的请求体
* scene: 业务场景login-登录, register-注册, reset-找回密码, bind-绑定手机等)
* @return ResultDomain<Boolean> 发送结果 * @return ResultDomain<Boolean> 发送结果
* @author yslg * @author yslg
* @since 2025-11-03 * @since 2025-11-03
@@ -203,6 +215,7 @@ public class AuthController {
ResultDomain<Map<String, String>> result = new ResultDomain<>(); ResultDomain<Map<String, String>> result = new ResultDomain<>();
String phone = requestBody.get("phone"); String phone = requestBody.get("phone");
String scene = requestBody.get("scene");
// 验证手机号格式 // 验证手机号格式
if (phone == null || phone.trim().isEmpty()) { if (phone == null || phone.trim().isEmpty()) {
@@ -215,10 +228,15 @@ public class AuthController {
return result; return result;
} }
// 验证场景参数
if (scene == null || scene.trim().isEmpty()) {
scene = "default";
}
// 检查是否频繁发送60秒内只能发送一次 // 检查是否频繁发送60秒内只能发送一次
String rateLimitKey = "sms:code:ratelimit:" + phone; String rateLimitKey = "sms:code:ratelimit:" + scene + ":" + phone;
if (redisService.hasKey(rateLimitKey)) { if (redisService.hasKey(rateLimitKey)) {
result.fail("验证码已发送,请勿重复发送"); result.fail("验证码已发送,请60秒后再试");
return result; return result;
} }
@@ -232,21 +250,25 @@ public class AuthController {
boolean success = smsUtils.sendVerificationCode(phone, code); boolean success = smsUtils.sendVerificationCode(phone, code);
if (success) { if (success) {
// 将验证码存储到Redis绑定sessionId有效期5分钟 // 将验证码存储到Redis绑定sessionId和业务场景有效期5分钟
String codeKey = "sms:code:" + sessionId; String codeKey = "sms:code:" + scene + ":" + sessionId;
String codeValue = phone + ":" + code; // 格式:手机号:验证码 String codeValue = phone + ":" + code;
redisService.set(codeKey, codeValue, 5, TimeUnit.MINUTES); redisService.set(codeKey, codeValue, 5, TimeUnit.MINUTES);
// 设置5分钟的发送频率限制 // 设置60秒的发送频率限制
redisService.set(rateLimitKey, "1", 5, TimeUnit.MINUTES); redisService.set(rateLimitKey, "1", 60, TimeUnit.SECONDS);
// 返回sessionId给前端 // 计算过期时间戳(当前时间 + 5分钟
long expireTime = System.currentTimeMillis() + (5 * 60 * 1000);
// 返回sessionId和过期时间给前端
Map<String, String> data = Map.of( Map<String, String> data = Map.of(
"sessionId", sessionId, "sessionId", sessionId,
"expireTime", String.valueOf(expireTime),
"message", "验证码已发送" "message", "验证码已发送"
); );
logger.info("短信验证码已发送,手机号: {}, sessionId: {}", phone, sessionId); logger.info("短信验证码已发送,手机号: {}, scene: {}, sessionId: {}, expireTime: {}", phone, scene, sessionId, expireTime);
result.success("验证码已发送", data); result.success("验证码已发送", data);
} else { } else {
result.fail("验证码发送失败,请稍后重试"); result.fail("验证码发送失败,请稍后重试");
@@ -337,8 +359,8 @@ public class AuthController {
return result; return result;
} }
// 通过sessionId验证手机验证码 // 通过sessionId验证手机验证码(注册场景)
String smsCodeKey = "sms:code:" + smsSessionId; String smsCodeKey = "sms:code:register:" + smsSessionId;
String storedSmsValue = (String) redisService.get(smsCodeKey); String storedSmsValue = (String) redisService.get(smsCodeKey);
if (storedSmsValue == null) { if (storedSmsValue == null) {
result.fail("验证码已过期,请重新获取"); result.fail("验证码已过期,请重新获取");
@@ -393,8 +415,8 @@ public class AuthController {
return result; return result;
} }
// 通过sessionId验证邮箱验证码 // 通过sessionId验证邮箱验证码(注册场景)
String emailCodeKey = "email:code:" + emailSessionId; String emailCodeKey = "email:code:register:" + emailSessionId;
String storedEmailValue = (String) redisService.get(emailCodeKey); String storedEmailValue = (String) redisService.get(emailCodeKey);
if (storedEmailValue == null) { if (storedEmailValue == null) {
result.fail("验证码已过期,请重新获取"); result.fail("验证码已过期,请重新获取");

View File

@@ -74,8 +74,8 @@ public class EmailLoginStrategy implements LoginStrategy {
return false; return false;
} }
// 从Redis获取验证码 // 从Redis获取验证码(登录场景)
String codeKey = "email:code:" + captchaId; String codeKey = "email:code:login:" + captchaId;
String storedValue = (String) redisService.get(codeKey); String storedValue = (String) redisService.get(codeKey);
if (storedValue == null) { if (storedValue == null) {

View File

@@ -74,8 +74,8 @@ public class PhoneLoginStrategy implements LoginStrategy {
return false; return false;
} }
// 从Redis获取验证码 // 从Redis获取验证码(登录场景)
String codeKey = "sms:code:" + captchaId; String codeKey = "sms:code:login:" + captchaId;
String storedValue = (String) redisService.get(codeKey); String storedValue = (String) redisService.get(codeKey);
if (storedValue == null) { if (storedValue == null) {

View File

@@ -61,20 +61,22 @@ export const authApi = {
/** /**
* 发送手机验证码 * 发送手机验证码
* @param phone 手机号 * @param phone 手机号
* @returns Promise<ResultDomain<{sessionId: string, message: string}>> * @param scene 业务场景login-登录, register-注册, reset-找回密码, bind-绑定手机等)
* @returns Promise<ResultDomain<{sessionId: string, expireTime: string, message: string}>>
*/ */
async sendSmsCode(phone: string): Promise<ResultDomain<{sessionId: string, message: string}>> { async sendSmsCode(phone: string, scene: string = 'default'): Promise<ResultDomain<{sessionId: string, expireTime: string, message: string}>> {
const response = await api.post<{sessionId: string, message: string}>('/auth/send-sms-code', { phone }); const response = await api.post<{sessionId: string, expireTime: string, message: string}>('/auth/send-sms-code', { phone, scene });
return response.data; return response.data;
}, },
/** /**
* 发送邮箱验证码 * 发送邮箱验证码
* @param email 邮箱 * @param email 邮箱
* @returns Promise<ResultDomain<{sessionId: string, message: string}>> * @param scene 业务场景login-登录, register-注册, reset-找回密码, bind-绑定邮箱等)
* @returns Promise<ResultDomain<{sessionId: string, expireTime: string, message: string}>>
*/ */
async sendEmailCode(email: string): Promise<ResultDomain<{sessionId: string, message: string}>> { async sendEmailCode(email: string, scene: string = 'default'): Promise<ResultDomain<{sessionId: string, expireTime: string, message: string}>> {
const response = await api.post<{sessionId: string, message: string}>('/auth/send-email-code', { email }); const response = await api.post<{sessionId: string, expireTime: string, message: string}>('/auth/send-email-code', { email, scene });
return response.data; return response.data;
} }
}; };

View File

@@ -320,7 +320,7 @@ const handleSendSmsCode = async () => {
} }
try { try {
const result = await authApi.sendSmsCode(forgotForm.phone); const result = await authApi.sendSmsCode(forgotForm.phone, 'reset');
if (result.code === 200 && result.data) { if (result.code === 200 && result.data) {
// 保存sessionId // 保存sessionId
forgotForm.smsSessionId = result.data.sessionId; forgotForm.smsSessionId = result.data.sessionId;
@@ -354,7 +354,7 @@ const handleSendEmailCode = async () => {
} }
try { try {
const result = await authApi.sendEmailCode(forgotForm.email); const result = await authApi.sendEmailCode(forgotForm.email, 'reset');
if (result.code === 200 && result.data) { if (result.code === 200 && result.data) {
// 保存sessionId // 保存sessionId
forgotForm.emailSessionId = result.data.sessionId; forgotForm.emailSessionId = result.data.sessionId;

View File

@@ -319,7 +319,7 @@ const handleSendSmsCode = async () => {
} }
try { try {
const result = await authApi.sendSmsCode(forgotForm.phone); const result = await authApi.sendSmsCode(forgotForm.phone, 'reset');
if (result.code === 200 && result.data) { if (result.code === 200 && result.data) {
// 保存sessionId // 保存sessionId
forgotForm.smsSessionId = result.data.sessionId; forgotForm.smsSessionId = result.data.sessionId;
@@ -353,7 +353,7 @@ const handleSendEmailCode = async () => {
} }
try { try {
const result = await authApi.sendEmailCode(forgotForm.email); const result = await authApi.sendEmailCode(forgotForm.email, 'reset');
if (result.code === 200 && result.data) { if (result.code === 200 && result.data) {
// 保存sessionId // 保存sessionId
forgotForm.emailSessionId = result.data.sessionId; forgotForm.emailSessionId = result.data.sessionId;

View File

@@ -358,7 +358,7 @@ const handleSendSmsCode = async () => {
} }
try { try {
const result = await authApi.sendSmsCode(loginForm.phone); const result = await authApi.sendSmsCode(loginForm.phone, 'login');
if (result.code === 200 && result.data) { if (result.code === 200 && result.data) {
// 保存sessionId // 保存sessionId
loginForm.captchaId = result.data.sessionId; loginForm.captchaId = result.data.sessionId;
@@ -396,7 +396,7 @@ const handleSendEmailCode = async () => {
} }
try { try {
const result = await authApi.sendEmailCode(loginForm.email); const result = await authApi.sendEmailCode(loginForm.email, 'login');
if (result.code === 200 && result.data) { if (result.code === 200 && result.data) {
// 保存sessionId // 保存sessionId
loginForm.captchaId = result.data.sessionId; loginForm.captchaId = result.data.sessionId;

View File

@@ -358,7 +358,7 @@ const handleSendSmsCode = async () => {
} }
try { try {
const result = await authApi.sendSmsCode(loginForm.phone); const result = await authApi.sendSmsCode(loginForm.phone, 'login');
if (result.code === 200 && result.data) { if (result.code === 200 && result.data) {
// 保存sessionId // 保存sessionId
loginForm.captchaId = result.data.sessionId; loginForm.captchaId = result.data.sessionId;
@@ -396,7 +396,7 @@ const handleSendEmailCode = async () => {
} }
try { try {
const result = await authApi.sendEmailCode(loginForm.email); const result = await authApi.sendEmailCode(loginForm.email, 'login');
if (result.code === 200 && result.data) { if (result.code === 200 && result.data) {
// 保存sessionId // 保存sessionId
loginForm.captchaId = result.data.sessionId; loginForm.captchaId = result.data.sessionId;

View File

@@ -387,7 +387,7 @@ const handleSendSmsCode = async () => {
} }
try { try {
const result = await authApi.sendSmsCode(registerForm.phone!); const result = await authApi.sendSmsCode(registerForm.phone!, 'register');
if (result.code === 200 && result.data) { if (result.code === 200 && result.data) {
// 保存sessionId // 保存sessionId
registerForm.smsSessionId = result.data.sessionId; registerForm.smsSessionId = result.data.sessionId;
@@ -421,7 +421,7 @@ const handleSendEmailCode = async () => {
} }
try { try {
const result = await authApi.sendEmailCode(registerForm.email!); const result = await authApi.sendEmailCode(registerForm.email!, 'register');
if (result.code === 200 && result.data) { if (result.code === 200 && result.data) {
// 保存sessionId // 保存sessionId
registerForm.emailSessionId = result.data.sessionId; registerForm.emailSessionId = result.data.sessionId;

View File

@@ -398,7 +398,7 @@ const handleSendSmsCode = async () => {
} }
try { try {
const result = await authApi.sendSmsCode(registerForm.phone!); const result = await authApi.sendSmsCode(registerForm.phone!, 'register');
if (result.code === 200 && result.data) { if (result.code === 200 && result.data) {
// 保存sessionId // 保存sessionId
registerForm.smsSessionId = result.data.sessionId; registerForm.smsSessionId = result.data.sessionId;
@@ -432,7 +432,7 @@ const handleSendEmailCode = async () => {
} }
try { try {
const result = await authApi.sendEmailCode(registerForm.email!); const result = await authApi.sendEmailCode(registerForm.email!, 'register');
if (result.code === 200 && result.data) { if (result.code === 200 && result.data) {
// 保存sessionId // 保存sessionId
registerForm.emailSessionId = result.data.sessionId; registerForm.emailSessionId = result.data.sessionId;