From 78ee5ed462b1c2798e681e3bce70767f6a3ec1b5 Mon Sep 17 00:00:00 2001 From: "sinjar.chen" Date: Wed, 7 Jul 2021 21:58:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E6=89=BE=E5=9B=9E?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/controller/LoginController.java | 116 ++++++++++++ .../greate/community/service/UserService.java | 65 +++++++ .../greate/community/util/CommunityUtil.java | 16 ++ src/main/resources/static/js/reset-pwd.js | 165 ++++++++++++++++++ src/main/resources/templates/site/login.html | 2 +- .../resources/templates/site/reset-pwd.html | 106 +++++++++++ 6 files changed, 469 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/static/js/reset-pwd.js create mode 100644 src/main/resources/templates/site/reset-pwd.html diff --git a/src/main/java/com/greate/community/controller/LoginController.java b/src/main/java/com/greate/community/controller/LoginController.java index 75ca7982..e5d88aaf 100644 --- a/src/main/java/com/greate/community/controller/LoginController.java +++ b/src/main/java/com/greate/community/controller/LoginController.java @@ -23,6 +23,8 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.awt.image.BufferedImage; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -64,6 +66,14 @@ public class LoginController implements CommunityConstant { return "site/login"; } + /** + * 进入重置密码界面 + */ + @GetMapping("/resetPwd") + public String getResetPwdPage() { + return "site/reset-pwd"; + } + /** * 注册用户 * @param model @@ -144,6 +154,27 @@ public class LoginController implements CommunityConstant { } } + /** + * 验证用户输入的图片验证码是否和redis中存入的是否相等 + * + * @param kaptchaOwner 从 cookie 中取出的 kaptchaOwner + * @param checkCode 用户输入的图片验证码 + * @return 失败则返回原因, 验证成功返回 "", + */ + private String checkKaptchaCode(String kaptchaOwner, String checkCode) { + if (StringUtils.isBlank(checkCode)) { + return "未发现输入的图片验证码"; + } + String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner); + String kaptchaValue = (String) redisTemplate.opsForValue().get(redisKey); + if (StringUtils.isBlank(kaptchaValue)) { + return "图片验证码过期"; + } else if (!kaptchaValue.equalsIgnoreCase(checkCode)) { + return "图片验证码错误"; + } + return ""; + } + /** * 用户登录 * @param username 用户名 @@ -206,4 +237,89 @@ public class LoginController implements CommunityConstant { return "redirect:/login"; } + /** + * 重置密码 + */ + @PostMapping("/resetPwd") + @ResponseBody + public Map resetPwd(@RequestParam("username") String username, + @RequestParam("password") String password, + @RequestParam("emailVerifyCode") String emailVerifyCode, + @RequestParam("kaptchaCode") String kaptcha, + Model model, + @CookieValue("kaptchaOwner") String kaptchaOwner) { + Map map = new HashMap<>(4); + // 检查图片验证码 + String kaptchaCheckRst = checkKaptchaCode(kaptchaOwner, kaptcha); + if (StringUtils.isNotBlank(kaptchaCheckRst)) { + map.put("status", "1"); + map.put("errMsg", kaptchaCheckRst); + } + // 检查邮件验证码 + String emailVerifyCodeCheckRst = checkRedisResetPwdEmailCode(username, emailVerifyCode); + if (StringUtils.isNotBlank(emailVerifyCodeCheckRst)) { + map.put("status", "1"); + map.put("errMsg", emailVerifyCodeCheckRst); + } + // 执行重置密码操作 + Map stringObjectMap = userService.doResetPwd(username, password); + String usernameMsg = (String) stringObjectMap.get("errMsg"); + if (StringUtils.isBlank(usernameMsg)) { + map.put("status", "0"); + map.put("msg", "重置密码成功!"); + map.put("target", "/login"); + } + return map; + } + + /** + * 发送邮件验证码(用于重置密码) + * + * @param kaptchaOwner 从 cookie 中取出的 kaptchaOwner + * @param kaptcha 用户输入的图片验证码 + * @param username 用户输入的需要找回的账号 + */ + @PostMapping("/sendEmailCodeForResetPwd") + @ResponseBody + public Map sendEmailCodeForResetPwd(Model model, @CookieValue("kaptchaOwner") String kaptchaOwner, + @RequestParam("kaptcha") String kaptcha, + @RequestParam("username") String username) { + Map map = new HashMap<>(3); + // 检查图片验证码 + String kaptchaCheckRst = checkKaptchaCode(kaptchaOwner, kaptcha); + if (StringUtils.isNotBlank(kaptchaCheckRst)) { + map.put("status", "1"); + map.put("errMsg", kaptchaCheckRst); + } + Map stringObjectMap = userService.doSendEmailCode4ResetPwd(username); + String usernameMsg = (String) stringObjectMap.get("errMsg"); + if (StringUtils.isBlank(usernameMsg)) { + map.put("status", "0"); + map.put("msg", "已经往您的邮箱发送了一封验证码邮件, 请查收!"); + } + return map; + } + + /** + * 检查 邮件 验证码 + * + * @param username 用户名 + * @param checkCode 用户输入的图片验证码 + * @return 验证成功 返回"", 失败则返回原因 + */ + private String checkRedisResetPwdEmailCode(String username, String checkCode) { + if (StringUtils.isBlank(checkCode)) { + return "未发现输入的邮件验证码"; + } + final String redisKey = "EmailCode4ResetPwd:" + username; + String emailVerifyCodeInRedis = (String) redisTemplate.opsForValue().get(redisKey); + if (StringUtils.isBlank(emailVerifyCodeInRedis)) { + return "邮件验证码已过期"; + } else if (!emailVerifyCodeInRedis.equalsIgnoreCase(checkCode)) { + return "邮件验证码错误"; + } + return ""; + } + + } diff --git a/src/main/java/com/greate/community/service/UserService.java b/src/main/java/com/greate/community/service/UserService.java index 7f3b652c..5d9b33c8 100644 --- a/src/main/java/com/greate/community/service/UserService.java +++ b/src/main/java/com/greate/community/service/UserService.java @@ -318,4 +318,69 @@ public class UserService implements CommunityConstant { return list; } + + /** + * 发送邮箱验证码 + * @param account 账户名, 目前是用户名 + * + * @return Map 返回错误提示消息,如果返回的 map 为空,则说明发送验证码成功 + */ + public Map doSendEmailCode4ResetPwd(String account) { + Map map = new HashMap<>(2); + User user = userMapper.selectByName(account); + if (user == null) { + map.put("errMsg", "未发现账号"); + return map; + } + final String email = user.getEmail(); + if (StringUtils.isBlank(email)) { + map.put("errMsg", "该账号未绑定邮箱"); + return map; + } + + // 生成6位验证码 + String randomCode = CommunityUtil.getRandomCode(6); + // 给注册用户发送激活邮件 + Context context = new Context(); + context.setVariable("email", "您的验证码是 " + randomCode); + // http://localhost:8080/echo/activation/用户id/激活码 + String url = domain + contextPath + "/activation/" + user.getId() + "/" + user.getActivationCode(); + context.setVariable("url", url); + String content = templateEngine.process("/mail/activation", context); + mailClient.sendMail(email,"重置 Echo 账号密码", content); + + final String redisKey = "EmailCode4ResetPwd:" + account; + + redisTemplate.opsForValue().set(redisKey, randomCode, 600, TimeUnit.SECONDS); + return map; + } + + /** + * 发送邮箱验证码 + * @param account 账户名, 目前是用户名 + * + * @return Map 返回错误提示消息,如果返回的 map 为空,则说明发送验证码成功 + */ + public Map doResetPwd(String account, String password) { + Map map = new HashMap<>(2); + if (StringUtils.isBlank(password)) { + map.put("errMsg", "密码不能为空"); + return map; + } + User user = userMapper.selectByName(account); + if (user == null) { + map.put("errMsg", "未发现账号"); + return map; + } + final String passwordEncode = CommunityUtil.md5(password + user.getSalt()); + int i = userMapper.updatePassword(user.getId(), passwordEncode); + if (i <= 0) { + map.put("errMsg", "修改数据库密码错误"); + } else { + clearCache(user.getId()); + } + return map; + } + + } diff --git a/src/main/java/com/greate/community/util/CommunityUtil.java b/src/main/java/com/greate/community/util/CommunityUtil.java index feff0883..2785023d 100644 --- a/src/main/java/com/greate/community/util/CommunityUtil.java +++ b/src/main/java/com/greate/community/util/CommunityUtil.java @@ -1,7 +1,9 @@ package com.greate.community.util; import com.alibaba.fastjson.JSONObject; +import org.apache.commons.lang3.RandomUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; import org.springframework.util.DigestUtils; import java.util.HashMap; @@ -69,6 +71,20 @@ public class CommunityUtil { return json.toJSONString(); } + /** + * 生成指定位数的数字随机数, 最高不超过 9 位 + * + * @param length + * @return + */ + public static String getRandomCode(int length) { + Validate.isTrue(length <= 9 && length > 0, "生成数字随机数长度范围应该在 1~9 内, 参数 length : %s", length); + int floor = (int) Math.pow(10, length - 1); + int codeNum = RandomUtils.nextInt(floor, floor * 10); + return Integer.toString(codeNum); + } + + /** * 测试 * @param args diff --git a/src/main/resources/static/js/reset-pwd.js b/src/main/resources/static/js/reset-pwd.js new file mode 100644 index 00000000..303024f4 --- /dev/null +++ b/src/main/resources/static/js/reset-pwd.js @@ -0,0 +1,165 @@ +var CONTEXT_PATH = ""; + +//V2 static +String.format = function () { + if (arguments.length == 0) { + return null; + } + let str = arguments[0]; + for (let i = 1; i < arguments.length; i++) { + let re = new RegExp('\\{' + i + '\\}', 'gm'); + str = str.replace(re, arguments[i]); + } + return str; +}; + +$.fn.parseForm=function(){ + let serializeObj={}; + let array=this.serializeArray(); + let str=this.serialize(); + $(array).each(function(){ + if(serializeObj[this.name]){ + if($.isArray(serializeObj[this.name])){ + serializeObj[this.name].push(this.value); + }else{ + serializeObj[this.name]=[serializeObj[this.name],this.value]; + } + }else{ + serializeObj[this.name]=this.value; + } + }); + return serializeObj; +}; + + +$(function(){ + $("#resetPwd").click(function(){ + if (check_data()) { + $.ajax({ + type : "post", + url : '/resetPwd', + data : $('#resetPwdForm').parseForm(), + success : function (result) { + if (result) { + if (result.status === '1') { + console.log('重置密码失败'); + console.log(result.errMsg); + } else if (result.status === '0') { + console.log('重置密码成功'); + if (result.msg) { + alert(result.msg); + location.href='/login'; + } + } else { + console.log(result); + } + } + } + }) + } else { + $("input").focus(clear_error); + } + }); +}); + +function check_data() { + if (!$("#password").val()) { + $("#password").addClass("is-invalid"); + return false; + } + if (!$("#confirm-password").val().trim()) { + $("#confirm-password").addClass("is-invalid"); + return false; + } + if (!$("#username").val()) { + $("#username").addClass("is-invalid"); + return false; + } + if (!$("#kaptchaCode").val().trim()) { + $("#kaptchaCode").addClass("is-invalid"); + return false; + } + if (!$("#emailVerifyCode").val().trim()) { + $("#emailVerifyCode").addClass("is-invalid"); + return false; + } + var pwd1 = $("#password").val(); + var pwd2 = $("#confirm-password").val(); + if(pwd1 !== pwd2) { + $("#confirm-password").addClass("is-invalid"); + return false; + } + return true; +} + +function clear_error() { + $(this).removeClass("is-invalid"); +} + +function refresh_kaptcha() { + var path = CONTEXT_PATH + "/kaptcha?p=" + Math.random(); + $("#kaptcha").attr("src", path); +} + +// 发送邮箱验证码功能 +function sendEmailCodeForResetPwd() { + var kaptchaCode = ($('#kaptchaCode').val() || '').trim(); + var username = ($('#username').val() || '').trim(); + if (kaptchaCode && username) { + $.ajax({ + type: 'POST', + url:"/sendEmailCodeForResetPwd", + data: { + username: username, + kaptcha: kaptchaCode + }, + success:function(result){ + if (result) { + if (result.status === '1') { + console.log('发送邮箱验证码失败'); + alert(result.errMsg); + } else if (result.status === '0') { + console.log('发送邮箱验证码成功'); + var div = $($('#hideDiv')[0]); + var clear = disableForAWhile(div, div); + alert(result.msg); + } else { + console.log(result); + } + } + } + }); + } else { + console.log('请输入完整的请求数据') + } +} + +// 倒计时 +// hideDiv 和 secondTextDiv 选择器, 仅仅支持jquery选择器的第一个元素 +function disableForAWhile(hideDiv, secondTextDiv, time, str, style) { + // 备份 + let tmpHtml = hideDiv[0].outerHTML; + let secondHtml = secondTextDiv[0].outerHTML; + time = time || 60; + str = str || '重新发送({1})'; + style = style || {'pointer-events': 'none', 'cursor': 'not-allowed'}; + hideDiv.css(style); + // clear function + secondTextDiv.text(String.format(str, time--)); + let clear = function (id) { + clearInterval(id); + hideDiv[0].outerHTML = tmpHtml; + secondTextDiv[0].outerHTML = secondHtml; + }; + let innerId = setInterval(() => { + secondTextDiv.text(String.format(str, time--)); + if (time <= 0) { + clear(innerId); + } + }, 1000); + return { + clear: () => { + clear(innerId) + } + }; +} diff --git a/src/main/resources/templates/site/login.html b/src/main/resources/templates/site/login.html index 836d8397..4b73baf9 100644 --- a/src/main/resources/templates/site/login.html +++ b/src/main/resources/templates/site/login.html @@ -57,7 +57,7 @@ - 忘记密码? + 忘记密码?
diff --git a/src/main/resources/templates/site/reset-pwd.html b/src/main/resources/templates/site/reset-pwd.html new file mode 100644 index 00000000..3755732d --- /dev/null +++ b/src/main/resources/templates/site/reset-pwd.html @@ -0,0 +1,106 @@ + + + + + + + + + + + Echo - 注册 + + +
+ +
+ + +
+
+

重置  密码

+
+
+ +
+ + +
+ 请输入请输入您的账号或绑定的邮箱 +
+
+
+
+ +
+ + +
+ 请输入新密码 +
+
+
+
+ +
+ +
+ 请确任两次密码是否匹配 +
+
+
+
+ +
+ +
+ 请输入图片验证码 +
+
+
+ + + +
+
+
+ +
+ +
+ 请输入邮箱验证码 +
+
+ +
+
+
+
+ +
+
+
+
+
+ + +
+
+ + + + + + +