(仿牛客论坛项目)06 - 忘记密码


前言

  1. 首先我们在登陆页面,点击忘记密码跳转到如下页面;
  2. 其次在如下页面输入邮箱(判断邮箱是否正确),点击获取验证码,会像邮箱中发送验证码;同时,这个验证码要自己保存一份以备验证;
  3. 填写邮箱和验证码之后,输入新密码,点击重置密码,修改数据库中用户的密码。

在这里插入图片描述


1、访问忘记密码页面

1.1 修改 LoginController 层

  1. 获取忘记密码的超链接请求,跳转到 forget.html 页面
@RequestMapping(path = "/forget",method = RequestMethod.GET)
public String getForgetPage(){
    return "site/forget";
}

1.2 修改 forget.html 页面

  1. 修改引入的样式表路径为 thymeleaf 格式的,包括所有使用相对路径的 CSS、JS 文件都修改为绝对路径格式,例如:th:href="@{/css/login.css}"
  2. 同时,头部文件进行代码复用;th:replace="index::header"

在这里插入图片描述

1.3 修改 index.html 界面

  1. 修改忘记密码跳转链接:
<a th:href="@{/forget}" class="text-danger float-right">忘记密码?</a>

1.4 测试页面

在登陆页面点击忘记密码:

在这里插入图片描述

在这里插入图片描述

2、向邮箱发送验证码

2.1 修改 forget.html 页面(错误排查中对 js 代码进行了修改)

  1. 没学过 jQuery ,所以用原生 JS 实现点击超链接发送请求(前端页面实在是困难):
<a href="#" th:onclick="get_code()" class="btn btn-info form-control">获取验证码</a>
<script>
    function get_code() {
        var email = document.getElementById("your-email");
        // 点击该超链接会发送请求给浏览器
        window.location.href = "getCode?email=" + email;
    }
</script>

2.2 UserService 层

  1. 实现获取验证码的方法,根据用户输入的邮箱;

    1. 判断邮箱是否为空;
    2. 判断邮箱是否存在被注册过;
    3. 判断邮箱的用户状态是否已经激活;
  2. 都没问题的话发送随机 6 位字符串验证码到指定邮箱(这里已经有做好的 html 模板可以使用,其中需要邮箱和验证码两个信息);

  3. 同时记录下来验证码和验证码过期时间,之后验证的时候使用;

//忘记密码之后给邮箱发送验证码
@Override
public Map<String, Object> getCode(String email) {
    Map<String,Object> map = new HashMap<>();
    //空值判断
    if (StringUtils.isBlank(email)){
        map.put("emailMsg","请输入邮箱!");
        return map;
    }
    //邮箱是否正确
    User user = userMapper.selectByEmail(email);
    if (user == null){
        map.put("emailMsg","该邮箱还未注册过,请注册后再使用!");
        return map;
    }
     //该用户还未激活
    if (user.getStatus() == 0){
        map.put("emailMsg","该邮箱还未激活,请到邮箱中激活后再使用!");
        return map;
    }
    //邮箱正确的情况下,发送验证码到邮箱
    Context context = new Context();
    context.setVariable("email",email);
    String code = CommunityUtil.generateUUID().substring(0,5);
    context.setVariable("code",code);
    String content = templateEngine.process("mail/forget", context);
    mailClient.sendMail(email, "牛客验证码", content);

    map.put("code",code);//map中存放一份,为了之后和用户输入的验证码进行对比
    map.put("expirationTime", LocalDateTime.now().plusMinutes(5L));//过期时间
    return map;
}

2.3 修改 mail/forget.html 页面

  1. 显示邮箱信息,和验证码信息:
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/>
    <title>牛客网-忘记密码</title>
</head>
<body>
	<div>
		<p>
			<b th:text="${email}">xxx@xxx.com</b>, 您好!
		</p>
		<p>
			您正在找回牛客账号的密码, 本次操作的验证码为 <b th:utext="${code}">u5s6dt</b> ,
			有效时间5分钟, 请您及时进行操作!
		</p>
	</div>
</body>
</html>

2.3 LoginController 层

  1. 实现获取验证码的方法,调用 Service 层获取验证码(底层实现了发送邮件的功能);
  2. 如果有错误信息,错误信息显示给客户端;
  3. 如果没有错误信息,告诉客户端验证码已发送的消息;
  4. 同时将验证码和过期时间记录在 session 中,以便后续和用户输入的信息作比较。
//忘记密码之后获取验证码
@RequestMapping(path = "/getCode", method = RequestMethod.GET)
public String getCode(String email, Model model,HttpSession session) {
    Map<String, Object> map = userService.getCode(email);
    if (map.containsKey("emailMsg")) {//有错误的情况下
        model.addAttribute("emailMsg", map.get("emailMsg"));
    } else {//正确的情况下,向邮箱发送了验证码
        model.addAttribute("msg", "验证码已经发送到您的邮箱,5分钟内有效!");
        //将验证码存放在 session 中,后序和用户输入的信息进行比较
        session.setAttribute("code",map.get("code"));
        //后序判断用户输入验证码的时候验证码是否已经过期
        session.setAttribute("expirationTime",map.get("expirationTime"));
    }
    return "site/forget";
}

2.4 修改 forget.html 页面

  1. 主要是邮箱和验证码的错误和正确信息回显:
<div class="form-group row">
    <label for="your-email" class="col-sm-2 col-form-label text-right">邮箱:</label>
    <div class="col-sm-10">
        <input type="email" th:class="|form-control ${emailMsg!=null?'is-invalid':''}|"
               name="email" th:value="${param.email}"
               id="your-email" placeholder="请输入您的邮箱!" required>
        <div class="invalid-feedback" th:text="${emailMsg}">
            该邮箱已被注册!
        </div>
    </div>
</div>
<div class="form-group row mt-4">
    <label for="verifycode" class="col-sm-2 col-form-label text-right">验证码:</label>
    <div class="col-sm-6">
        <input type="text" th:class="|form-control ${codeMsg!=null?'is-invalid':''}|"
               name="verifycode"
               id="verifycode" placeholder="请输入验证码!">
        <div class="invalid-feedback" th:text="${codeMsg}">
            验证码不正确!
        </div>
    </div>
    <div class="col-sm-4">
       <a href="javascript:void(0)" th:onclick="get_code()" class="btn btn-info form-control">获取验证码</a>
    </div>
</div>

2.5 测试页面

  1. 空值测试(前端逻辑过不去):

在这里插入图片描述

  1. 未注册的邮箱:

在这里插入图片描述

  1. 未激活的邮箱:

在这里插入图片描述

  1. 正确情况下:

在这里插入图片描述

在这里插入图片描述

2.6 错误排查

2.6.1 邮箱未输入的情况下,点击获取验证码出现如下错误:

  1. 原因是前端页面设置了只有在表单提交的时候,内容为空的警告,没有其他空警告功能,所以我们需要自己实现一个;

  2. 同时,我们通过 document.getElementById("your-email"); 获取到的是一个 DOM 对象,而不是 input 标签中的值,需要进行修改;

java.lang.IllegalArgumentException: Invalid character found in the request target [/community/getCode?email=[object%20HTMLInputElement] ]. The valid characters are defined in RFC 7230 and RFC 3986
	at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:494)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:271)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1787)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.base/java.lang.Thread.run(Thread.java:833)

在这里插入图片描述

  1. js 功能的实现:
<script>
    function get_code() {
        var email = $("#your-email").val();	//获取input框内的值;
        if (email == null || email == ""){
            alert("邮箱未填写!");	//如果值为空,提示用户填写
            return;
        }else{
            // 点击该超链接会发送请求给浏览器
            window.location = "getCode?email=" + email;
        }
    }
</script>

3、重置密码功能实现

3.1 UserService 层

  1. 实现忘记密码功能;
  2. 空值处理,验证码是否正确,是否过期;
  3. 邮箱和验证码没有问题的情况下修改当前用户的密码;
//忘记密码
@Override
public Map<String, Object> forget(String email, String verifycode, String password, HttpSession session){
    Map<String,Object> map = new HashMap<>();
    //空值处理
    if (StringUtils.isBlank(email)){
        map.put("emailMsg","请输入邮箱!");
        return map;
    }
    if (StringUtils.isBlank(verifycode)){
        map.put("codeMsg","请输入验证码!");
        return map;
    }
    if (StringUtils.isBlank(password)){
        map.put("passwordMsg","请输入新密码!");
        return map;
    }
    /*
    //验证验证码是否正确
    if (!verifycode.equals(session.getAttribute("code"))){
        map.put("codeMsg","输入的验证码不正确!");
        return map;
    }
    //验证码是否过期
    if (LocalDateTime.now().isAfter((LocalDateTime)session.getAttribute("expirationTime"))){
        map.put("codeMsg","输入的验证码已过期,请重新获取验证码!");
        return map;
    }
    */
    //邮箱在获取验证码那一步已经验证过了,是有效的邮箱,且验证码也有效
    User user = userMapper.selectByEmail(email);
    password = CommunityUtil.md5(password + user.getSalt());
    userMapper.updatePassword(user.getId(),password);

    return map;
}

解释:

  • 由于 session 最好是在表现层使用,所以这里判断验证码放到表现层去判断,因为涉及到调用 session。

3.2 LoginController 层

  1. 判断验证码是否有效,是否已经过期;
  2. 没有问题的情况下,调用 service 层去更新用户密码;
//忘记密码
@RequestMapping(path = "/forget", method = RequestMethod.POST)
public String forget(Model model, String email, String verifycode, String password, HttpSession session) {
    //验证验证码是否正确
    if (!verifycode.equals(session.getAttribute("code"))) {
        model.addAttribute("codeMsg", "输入的验证码不正确!");
        return "site/forget";
    }
    //验证码是否过期
    if (LocalDateTime.now().isAfter((LocalDateTime) session.getAttribute("expirationTime"))) {
        model.addAttribute("codeMsg", "输入的验证码已过期,请重新获取验证码!");
        return "site/forget";
    }
    Map<String, Object> map = userService.forget(email, verifycode, password);
    if (map == null || map.isEmpty()) {
        model.addAttribute("msg", "密码修改成功,可以使用新密码登录了!");
        model.addAttribute("target", "/login");
        return "site/operate-result";
    } else {
        model.addAttribute("emailMsg", map.get("emailMsg"));
        model.addAttribute("codeMsg", map.get("codeMsg"));
        model.addAttribute("passwordMsg", map.get("passwordMsg"));
        return "/site/forget";
    }
}

3.3 修改 forget.html 页面

  1. 每一项输入的 name 属性;
  2. 已填充信息回显;
  3. 错误信息显示;
  4. 错误样式显示
<form class="mt-5" th:action="@{/forget}" method="post">
    <div class="form-group row">
        <label for="your-email" class="col-sm-2 col-form-label text-right">邮箱:</label>
        <div class="col-sm-10">
            <input type="email" th:class="|form-control ${emailMsg!=null?'is-invalid':''}|"
                   name="email" th:value="${param.email}"
                   id="your-email" placeholder="请输入您的邮箱!" required>
            <div class="invalid-feedback" th:text="${emailMsg}">
                该邮箱已被注册!
            </div>
        </div>
    </div>
    <div class="form-group row mt-4">
        <label for="verifycode" class="col-sm-2 col-form-label text-right">验证码:</label>
        <div class="col-sm-6">
            <input type="text" th:class="|form-control ${codeMsg!=null?'is-invalid':''}|"
                   name="verifycode"
                   id="verifycode" placeholder="请输入验证码!">
            <div class="invalid-feedback" th:text="${codeMsg}">
                验证码不正确!
            </div>
        </div>
        <div class="col-sm-4">
            <a href="javascript:void(0)" th:onclick="get_code()" class="btn btn-info form-control">获取验证码</a>
        </div>
    </div>
    <div class="form-group row mt-4">
        <label for="your-password" class="col-sm-2 col-form-label text-right">新密码:</label>
        <div class="col-sm-10">
            <input type="password" th:class="|form-control ${passwordMsg!=null?'is-invalid':''}|"
                   name="password" th:value="${param.password}"
                   id="your-password" placeholder="请输入新的密码!" required>
            <div class="invalid-feedback" th:text="${passwordMsg}">
                密码长度不能小于8!
            </div>
        </div>
    </div>
    <div class="form-group row mt-4">
        <div class="col-sm-2"></div>
        <div class="col-sm-10 text-center">
            <button type="submit" class="btn btn-info text-white form-control">重置密码</button>
        </div>
    </div>
</form>

3.4 测试页面

  1. 空值就不演示了,前端逻辑会实现;
  2. 验证码不正确:

在这里插入图片描述

  1. 验证码过期:

在这里插入图片描述

  1. 正确情况下:

在这里插入图片描述
在这里插入图片描述

参考文献

  1. 表单提交和超链接请求传递参数的几种方式
  2. java8 — 新日期时间API篇
  3. js获取input里的值,并判断是否为空,如果不为空,提交表单
  • 6
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Kafka是一个高性能的分布式消息队列系统,可以实现高吞吐量、低延迟的消息传递。它支持点对点和发布-订阅两种消息传递模式。在仿牛客项目中使用Kafka可以实现消息的异步处理和分布式架构。 使用Kafka的第一步是创建一个主题(topic),主题既是消息的类别,也是消息在Kafka中的存储位置。可以使用命令行工具kafka-topics.bat来创建主题。例如,可以使用以下命令来创建一个名为test的主题: bin\windows\kafka-topics.bat --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic test 上述命令中,--bootstrap-server参数指定了Kafka服务器的地址和端口,--replication-factor参数指定了主题的副本数,--partitions参数指定了主题的分区数。创建主题后,可以向主题中发送消息,并由消费者进行消费。 要列出已经存在的主题,可以使用以下命令: kafka-topics.bat --list --bootstrap-server localhost:9092 需要注意的是,以上命令中的localhost:9092是Kafka服务器的地址和端口,根据实际情况进行修改。 总结起来,在仿牛客项目中使用Kafka,首先需要创建一个主题,然后可以使用相关命令行工具进行消息的发送和消费。这样可以实现消息的异步处理和分布式架构。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [仿牛客论坛项目学习记录——5 Kafka 构建TB级异步消息系统](https://blog.csdn.net/dadayangpei/article/details/127173098)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值