From f7b0dcd1205bdac152872916db1f1aeb96922c82 Mon Sep 17 00:00:00 2001 From: wangys <3401275564@qq.com> Date: Tue, 16 Dec 2025 18:26:52 +0800 Subject: [PATCH] =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xyzh/auth/controller/AuthController.java | 70 ++++++++++++------- .../strategy/impl/EmailLoginStrategy.java | 4 +- .../strategy/impl/PhoneLoginStrategy.java | 4 +- schoolNewsWeb/src/apis/system/auth.ts | 14 ++-- .../public/login/ForgotPassword.mobile.vue | 4 +- .../src/views/public/login/ForgotPassword.vue | 4 +- .../src/views/public/login/Login.mobile.vue | 4 +- .../src/views/public/login/Login.vue | 4 +- .../views/public/login/Register.mobile.vue | 4 +- .../src/views/public/login/Register.vue | 4 +- 10 files changed, 70 insertions(+), 46 deletions(-) diff --git a/schoolNewsServ/auth/src/main/java/org/xyzh/auth/controller/AuthController.java b/schoolNewsServ/auth/src/main/java/org/xyzh/auth/controller/AuthController.java index d52fa3b..9c7a9a9 100644 --- a/schoolNewsServ/auth/src/main/java/org/xyzh/auth/controller/AuthController.java +++ b/schoolNewsServ/auth/src/main/java/org/xyzh/auth/controller/AuthController.java @@ -128,7 +128,8 @@ public class AuthController { /** * @description 发送邮箱验证码 - * @param requestBody 包含email字段的请求体 + * @param requestBody 包含email和scene字段的请求体 + * scene: 业务场景(login-登录, register-注册, reset-找回密码, bind-绑定邮箱等) * @return ResultDomain 发送结果 * @author yslg * @since 2025-11-03 @@ -138,6 +139,7 @@ public class AuthController { ResultDomain> result = new ResultDomain<>(); String email = requestBody.get("email"); + String scene = requestBody.get("scene"); // 验证邮箱格式 if (email == null || email.trim().isEmpty()) { @@ -151,10 +153,15 @@ public class AuthController { return result; } + // 验证场景参数 + if (scene == null || scene.trim().isEmpty()) { + scene = "default"; + } + // 检查是否频繁发送(60秒内只能发送一次) - String rateLimitKey = "email:code:ratelimit:" + email; + String rateLimitKey = "email:code:ratelimit:" + scene + ":" + email; if (redisService.hasKey(rateLimitKey)) { - result.fail("验证码已发送,请勿重复发送"); + result.fail("验证码已发送,请60秒后再试"); return result; } @@ -168,21 +175,25 @@ public class AuthController { boolean success = emailUtils.sendVerificationCode(email, code); if (success) { - // 将验证码存储到Redis,绑定sessionId,有效期5分钟 - String codeKey = "email:code:" + sessionId; - String codeValue = email + ":" + code; // 格式:邮箱:验证码 + // 将验证码存储到Redis,绑定sessionId和业务场景,有效期5分钟 + String codeKey = "email:code:" + scene + ":" + sessionId; + String codeValue = email + ":" + code; redisService.set(codeKey, codeValue, 5, TimeUnit.MINUTES); - // 设置5分钟的发送频率限制 - redisService.set(rateLimitKey, "1", 5, TimeUnit.MINUTES); + // 设置60秒的发送频率限制 + redisService.set(rateLimitKey, "1", 60, TimeUnit.SECONDS); - // 返回sessionId给前端 + // 计算过期时间戳(当前时间 + 5分钟) + long expireTime = System.currentTimeMillis() + (5 * 60 * 1000); + + // 返回sessionId和过期时间给前端 Map data = Map.of( "sessionId", sessionId, + "expireTime", String.valueOf(expireTime), "message", "验证码已发送到邮箱" ); - logger.info("邮箱验证码已发送,邮箱: {}, sessionId: {}", email, sessionId); + logger.info("邮箱验证码已发送,邮箱: {}, scene: {}, sessionId: {}, expireTime: {}", email, scene, sessionId, expireTime); result.success("验证码已发送到邮箱", data); } else { result.fail("验证码发送失败,请稍后重试"); @@ -193,7 +204,8 @@ public class AuthController { /** * @description 发送手机验证码 - * @param requestBody 包含phone字段的请求体 + * @param requestBody 包含phone和scene字段的请求体 + * scene: 业务场景(login-登录, register-注册, reset-找回密码, bind-绑定手机等) * @return ResultDomain 发送结果 * @author yslg * @since 2025-11-03 @@ -203,6 +215,7 @@ public class AuthController { ResultDomain> result = new ResultDomain<>(); String phone = requestBody.get("phone"); + String scene = requestBody.get("scene"); // 验证手机号格式 if (phone == null || phone.trim().isEmpty()) { @@ -215,10 +228,15 @@ public class AuthController { return result; } + // 验证场景参数 + if (scene == null || scene.trim().isEmpty()) { + scene = "default"; + } + // 检查是否频繁发送(60秒内只能发送一次) - String rateLimitKey = "sms:code:ratelimit:" + phone; + String rateLimitKey = "sms:code:ratelimit:" + scene + ":" + phone; if (redisService.hasKey(rateLimitKey)) { - result.fail("验证码已发送,请勿重复发送"); + result.fail("验证码已发送,请60秒后再试"); return result; } @@ -232,21 +250,25 @@ public class AuthController { boolean success = smsUtils.sendVerificationCode(phone, code); if (success) { - // 将验证码存储到Redis,绑定sessionId,有效期5分钟 - String codeKey = "sms:code:" + sessionId; - String codeValue = phone + ":" + code; // 格式:手机号:验证码 + // 将验证码存储到Redis,绑定sessionId和业务场景,有效期5分钟 + String codeKey = "sms:code:" + scene + ":" + sessionId; + String codeValue = phone + ":" + code; redisService.set(codeKey, codeValue, 5, TimeUnit.MINUTES); - // 设置5分钟的发送频率限制 - redisService.set(rateLimitKey, "1", 5, TimeUnit.MINUTES); + // 设置60秒的发送频率限制 + redisService.set(rateLimitKey, "1", 60, TimeUnit.SECONDS); - // 返回sessionId给前端 + // 计算过期时间戳(当前时间 + 5分钟) + long expireTime = System.currentTimeMillis() + (5 * 60 * 1000); + + // 返回sessionId和过期时间给前端 Map data = Map.of( "sessionId", sessionId, + "expireTime", String.valueOf(expireTime), "message", "验证码已发送" ); - logger.info("短信验证码已发送,手机号: {}, sessionId: {}", phone, sessionId); + logger.info("短信验证码已发送,手机号: {}, scene: {}, sessionId: {}, expireTime: {}", phone, scene, sessionId, expireTime); result.success("验证码已发送", data); } else { result.fail("验证码发送失败,请稍后重试"); @@ -337,8 +359,8 @@ public class AuthController { return result; } - // 通过sessionId验证手机验证码 - String smsCodeKey = "sms:code:" + smsSessionId; + // 通过sessionId验证手机验证码(注册场景) + String smsCodeKey = "sms:code:register:" + smsSessionId; String storedSmsValue = (String) redisService.get(smsCodeKey); if (storedSmsValue == null) { result.fail("验证码已过期,请重新获取"); @@ -393,8 +415,8 @@ public class AuthController { return result; } - // 通过sessionId验证邮箱验证码 - String emailCodeKey = "email:code:" + emailSessionId; + // 通过sessionId验证邮箱验证码(注册场景) + String emailCodeKey = "email:code:register:" + emailSessionId; String storedEmailValue = (String) redisService.get(emailCodeKey); if (storedEmailValue == null) { result.fail("验证码已过期,请重新获取"); diff --git a/schoolNewsServ/auth/src/main/java/org/xyzh/auth/strategy/impl/EmailLoginStrategy.java b/schoolNewsServ/auth/src/main/java/org/xyzh/auth/strategy/impl/EmailLoginStrategy.java index a91f10d..2d62899 100644 --- a/schoolNewsServ/auth/src/main/java/org/xyzh/auth/strategy/impl/EmailLoginStrategy.java +++ b/schoolNewsServ/auth/src/main/java/org/xyzh/auth/strategy/impl/EmailLoginStrategy.java @@ -74,8 +74,8 @@ public class EmailLoginStrategy implements LoginStrategy { return false; } - // 从Redis获取验证码 - String codeKey = "email:code:" + captchaId; + // 从Redis获取验证码(登录场景) + String codeKey = "email:code:login:" + captchaId; String storedValue = (String) redisService.get(codeKey); if (storedValue == null) { diff --git a/schoolNewsServ/auth/src/main/java/org/xyzh/auth/strategy/impl/PhoneLoginStrategy.java b/schoolNewsServ/auth/src/main/java/org/xyzh/auth/strategy/impl/PhoneLoginStrategy.java index da3502f..3133f79 100644 --- a/schoolNewsServ/auth/src/main/java/org/xyzh/auth/strategy/impl/PhoneLoginStrategy.java +++ b/schoolNewsServ/auth/src/main/java/org/xyzh/auth/strategy/impl/PhoneLoginStrategy.java @@ -74,8 +74,8 @@ public class PhoneLoginStrategy implements LoginStrategy { return false; } - // 从Redis获取验证码 - String codeKey = "sms:code:" + captchaId; + // 从Redis获取验证码(登录场景) + String codeKey = "sms:code:login:" + captchaId; String storedValue = (String) redisService.get(codeKey); if (storedValue == null) { diff --git a/schoolNewsWeb/src/apis/system/auth.ts b/schoolNewsWeb/src/apis/system/auth.ts index 2e1eaf7..75c3158 100644 --- a/schoolNewsWeb/src/apis/system/auth.ts +++ b/schoolNewsWeb/src/apis/system/auth.ts @@ -61,20 +61,22 @@ export const authApi = { /** * 发送手机验证码 * @param phone 手机号 - * @returns Promise> + * @param scene 业务场景(login-登录, register-注册, reset-找回密码, bind-绑定手机等) + * @returns Promise> */ - async sendSmsCode(phone: string): Promise> { - const response = await api.post<{sessionId: string, message: string}>('/auth/send-sms-code', { phone }); + async sendSmsCode(phone: string, scene: string = 'default'): Promise> { + const response = await api.post<{sessionId: string, expireTime: string, message: string}>('/auth/send-sms-code', { phone, scene }); return response.data; }, /** * 发送邮箱验证码 * @param email 邮箱 - * @returns Promise> + * @param scene 业务场景(login-登录, register-注册, reset-找回密码, bind-绑定邮箱等) + * @returns Promise> */ - async sendEmailCode(email: string): Promise> { - const response = await api.post<{sessionId: string, message: string}>('/auth/send-email-code', { email }); + async sendEmailCode(email: string, scene: string = 'default'): Promise> { + const response = await api.post<{sessionId: string, expireTime: string, message: string}>('/auth/send-email-code', { email, scene }); return response.data; } }; diff --git a/schoolNewsWeb/src/views/public/login/ForgotPassword.mobile.vue b/schoolNewsWeb/src/views/public/login/ForgotPassword.mobile.vue index e3c14c3..b8b5a48 100644 --- a/schoolNewsWeb/src/views/public/login/ForgotPassword.mobile.vue +++ b/schoolNewsWeb/src/views/public/login/ForgotPassword.mobile.vue @@ -320,7 +320,7 @@ const handleSendSmsCode = async () => { } try { - const result = await authApi.sendSmsCode(forgotForm.phone); + const result = await authApi.sendSmsCode(forgotForm.phone, 'reset'); if (result.code === 200 && result.data) { // 保存sessionId forgotForm.smsSessionId = result.data.sessionId; @@ -354,7 +354,7 @@ const handleSendEmailCode = async () => { } try { - const result = await authApi.sendEmailCode(forgotForm.email); + const result = await authApi.sendEmailCode(forgotForm.email, 'reset'); if (result.code === 200 && result.data) { // 保存sessionId forgotForm.emailSessionId = result.data.sessionId; diff --git a/schoolNewsWeb/src/views/public/login/ForgotPassword.vue b/schoolNewsWeb/src/views/public/login/ForgotPassword.vue index 2c196a4..5ea3039 100644 --- a/schoolNewsWeb/src/views/public/login/ForgotPassword.vue +++ b/schoolNewsWeb/src/views/public/login/ForgotPassword.vue @@ -319,7 +319,7 @@ const handleSendSmsCode = async () => { } try { - const result = await authApi.sendSmsCode(forgotForm.phone); + const result = await authApi.sendSmsCode(forgotForm.phone, 'reset'); if (result.code === 200 && result.data) { // 保存sessionId forgotForm.smsSessionId = result.data.sessionId; @@ -353,7 +353,7 @@ const handleSendEmailCode = async () => { } try { - const result = await authApi.sendEmailCode(forgotForm.email); + const result = await authApi.sendEmailCode(forgotForm.email, 'reset'); if (result.code === 200 && result.data) { // 保存sessionId forgotForm.emailSessionId = result.data.sessionId; diff --git a/schoolNewsWeb/src/views/public/login/Login.mobile.vue b/schoolNewsWeb/src/views/public/login/Login.mobile.vue index 28e80f6..d90d224 100644 --- a/schoolNewsWeb/src/views/public/login/Login.mobile.vue +++ b/schoolNewsWeb/src/views/public/login/Login.mobile.vue @@ -358,7 +358,7 @@ const handleSendSmsCode = async () => { } try { - const result = await authApi.sendSmsCode(loginForm.phone); + const result = await authApi.sendSmsCode(loginForm.phone, 'login'); if (result.code === 200 && result.data) { // 保存sessionId loginForm.captchaId = result.data.sessionId; @@ -396,7 +396,7 @@ const handleSendEmailCode = async () => { } try { - const result = await authApi.sendEmailCode(loginForm.email); + const result = await authApi.sendEmailCode(loginForm.email, 'login'); if (result.code === 200 && result.data) { // 保存sessionId loginForm.captchaId = result.data.sessionId; diff --git a/schoolNewsWeb/src/views/public/login/Login.vue b/schoolNewsWeb/src/views/public/login/Login.vue index 6092a6d..976e4ed 100644 --- a/schoolNewsWeb/src/views/public/login/Login.vue +++ b/schoolNewsWeb/src/views/public/login/Login.vue @@ -358,7 +358,7 @@ const handleSendSmsCode = async () => { } try { - const result = await authApi.sendSmsCode(loginForm.phone); + const result = await authApi.sendSmsCode(loginForm.phone, 'login'); if (result.code === 200 && result.data) { // 保存sessionId loginForm.captchaId = result.data.sessionId; @@ -396,7 +396,7 @@ const handleSendEmailCode = async () => { } try { - const result = await authApi.sendEmailCode(loginForm.email); + const result = await authApi.sendEmailCode(loginForm.email, 'login'); if (result.code === 200 && result.data) { // 保存sessionId loginForm.captchaId = result.data.sessionId; diff --git a/schoolNewsWeb/src/views/public/login/Register.mobile.vue b/schoolNewsWeb/src/views/public/login/Register.mobile.vue index e68e2dd..cdd4688 100644 --- a/schoolNewsWeb/src/views/public/login/Register.mobile.vue +++ b/schoolNewsWeb/src/views/public/login/Register.mobile.vue @@ -387,7 +387,7 @@ const handleSendSmsCode = async () => { } try { - const result = await authApi.sendSmsCode(registerForm.phone!); + const result = await authApi.sendSmsCode(registerForm.phone!, 'register'); if (result.code === 200 && result.data) { // 保存sessionId registerForm.smsSessionId = result.data.sessionId; @@ -421,7 +421,7 @@ const handleSendEmailCode = async () => { } try { - const result = await authApi.sendEmailCode(registerForm.email!); + const result = await authApi.sendEmailCode(registerForm.email!, 'register'); if (result.code === 200 && result.data) { // 保存sessionId registerForm.emailSessionId = result.data.sessionId; diff --git a/schoolNewsWeb/src/views/public/login/Register.vue b/schoolNewsWeb/src/views/public/login/Register.vue index d444557..69f17c3 100644 --- a/schoolNewsWeb/src/views/public/login/Register.vue +++ b/schoolNewsWeb/src/views/public/login/Register.vue @@ -398,7 +398,7 @@ const handleSendSmsCode = async () => { } try { - const result = await authApi.sendSmsCode(registerForm.phone!); + const result = await authApi.sendSmsCode(registerForm.phone!, 'register'); if (result.code === 200 && result.data) { // 保存sessionId registerForm.smsSessionId = result.data.sessionId; @@ -432,7 +432,7 @@ const handleSendEmailCode = async () => { } try { - const result = await authApi.sendEmailCode(registerForm.email!); + const result = await authApi.sendEmailCode(registerForm.email!, 'register'); if (result.code === 200 && result.data) { // 保存sessionId registerForm.emailSessionId = result.data.sessionId;