使用 Spring Email 和 Thymeleaf 技术,向新注册用户发送激活邮件(二)

本篇博客对应“2.2 开发注册功能”小结
对应视频:
开发注册功能
开发注册功能-续

注册功能是相对比较复制的功能,对于一个相对复杂的功能,可以把这个功能进行拆解。把这个功能的流程想清楚,就知道怎么拆解了:
在这里插入图片描述
也可以按照请求进行拆解,注册过程一共发生三次请求,对应服务器产生三次响应:
在这里插入图片描述

我们一次请求,一次请求的把它搞定,就可以开发出整个功能了。
每一次请求按照:数据访问层、业务层、视图层。三层架构进行实现。
当然,有一些功能可能只有其中的一层或两层,写代码的时候就知道了。

访问注册页面

点击顶部区域的链接,打开注册页面。

新建一个LoginController
在这里插入图片描述

创建处理获取注册页面,返回注册视图的方法

    @RequestMapping(path = "/register", method = RequestMethod.GET)public String getRegisterPage() {return "/site/register";}

需要对register模板进行修改:
在这里插入图片描述

提交注册数据

准备工作

导入工具包Commons Lang,该工具包可以帮助我们判断字符串、集合是否为空、数据是否符合规范等其他功能。
引入mavaen依赖

		<dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.9</version></dependency>

引入jar包之后,还需要在配置文件中,把网站的域名配置号。

为什么要配置域名呢?

因为注册的过程中要发邮件,邮件里面得带上激活链接,这个激活链接得链接到我们的网站,而这个链接在开发、 测试、上线阶段是不同的,所以我们需要把他做成可配的。
application.properties文件中加入:

community:path:domain: http://localhost:8080

这三个都是自定义的,在程序里面用 @Value(“${属性名}”) 获取即可。

再写一个工具类,在工具类里面提供两个方法,注册的时候方便调用。
util包下新建一个工具类CommunityUtil
在这里插入图片描述

提供静态的方法:
生成随机字符串:生成激活码,每次是一个随机的字符串;给上传头像或上传文件的功能,每次上传时需要给图片或文件生成一个随机的名字,防止重复。使用java util包下的UUID类生成随机字符串,这是java自带的功能。
UUID生成的随机字符串会有横线,我们不需要

    // 生成随机字符串public static String generateUUID() {return UUID.randomUUID().toString().replaceAll("-", "");}

除此之外,我们还要封装一个方法,叫做MD5加密,采用MD5算法对密码进行加密。

为什么要加密?

因为用户在注册的时候,提交的密码是明文形式的,我们在存储到数据库中,需要将密码加密,这样,即使数据库泄露,别人也不会知道用户的密码是什么。
MD5加密算法的特点:

  • 任意长度的信息,经处理后,输出为128位的信息
  • 不同的输入得到不同的结果,唯一性
  • 不可逆性,由于通过散列函数,hash算法,在计算过程中,原文的信息是部分丢失了的。
    MD5加密算法只能加密,不能解密,而且每次加密都是这个值。
    例如: hello --> dhasdhqofhasjfdhas
    每次hello对应的密文都是dhasdhqofhasjfdhas,而且不可以解密。
    如果用户密码设置的过于简单,比如就是hello。盗取密码的黑客也会知道dhasdhqofhasjfdhas,因为他有一个简单密码的库,hello啊生日啊都包含在内,他也会把这个明文加密成密文,所以进行破解。
    因此不管用户输入的是什么密码,都加上一个随机的字符串,例如hello + 3edr4, 那么加密之后的密文就是:dasdjoqiwhdoqwhfdsh(假设),由于3edr4是随机的,黑客密码库中没有对应的明文-密文记录,所以是很难进行破解的。提高了用户密码的安全性。

为什么要加盐?

加盐表示的含义是加噪声,因为人们在设置密码时,通常都是在某个长度之内,不会过于复杂,虽然MD5本时时不可逆的,无法通过密文知道原文是什么。但是攻击者可以构造一个对照表,将明文和密文全部列举出来存到一个对照表中,然后采用穷举法,一个一个比较密文是哪一个,如果有密文是相同的,就可以去对照表中查明文,由于前面说的MD5唯一性的特性,这个明文一定是用户输入的密码,这就进行了破解。
加盐操作可以预防这个攻击方式,通过加噪声数据,可以极大的增加密码的随机性和复杂性,如果要使用对照表穷举的方式,需要消耗大量的计算机资源,这在现实中是不可行的。

用spring自带的工具就可以实现MD5加密,org.springframework.util.DigestUtils。加密方法如下:

    // MD5加密// hello -> abc123def456// hello + 3e4a8 -> abc123def456abcpublic static String md5(String key) {/** 判断key是否为空,空串、null、空格* */if (StringUtils.isBlank(key)) {return null;}return DigestUtils.md5DigestAsHex(key.getBytes());}

有了这些准备之后,可以开发真正的注册业务了。

通过表单提交数据

在UserService中写逻辑

需要注入的对象:

    @Autowiredprivate UserMapper userMapper;@Autowiredprivate MailClient mailClient;@Autowiredprivate TemplateEngine templateEngine;@Value("${community.path.domain}")private String domain;@Value("${server.servlet.context-path}")private String contextPath;

创建register方法:

public Map<String, Object> register(User user) {}

空值处理

        // 空值处理if (user == null) {throw new IllegalArgumentException("参数不能为空!");}if (StringUtils.isBlank(user.getUsername())) {map.put("usernameMsg", "账号不能为空!");return map;}if (StringUtils.isBlank(user.getPassword())) {map.put("passwordMsg", "密码不能为空!");return map;}if (StringUtils.isBlank(user.getEmail())) {map.put("emailMsg", "邮箱不能为空!");return map;}

服务端验证账号是否已存在、邮箱是否已存在

        User u = userMapper.selectByName(user.getUsername());if (u != null) {map.put("usernameMsg", "该账号已存在!");return map;}// 验证邮箱u = userMapper.selectByEmail(user.getEmail());if (u != null) {map.put("emailMsg", "该邮箱已被注册!");return map;}

注册用户

        // 注册用户user.setSalt(CommunityUtil.generateUUID().substring(0, 5));user.setPassword(CommunityUtil.md5(user.getPassword() + user.getSalt()));user.setType(0);user.setStatus(0);user.setActivationCode(CommunityUtil.generateUUID());user.setHeaderUrl(String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));user.setCreateTime(new Date());userMapper.insertUser(user);

知道了为什么要加盐之后,我们来看为什么需要 user.setSalt(),将这个salt值保存下来?

因为在用户成功注册,下一次登录的时候,用户需要再次输入账号以及明文的密码,但此时,数据库存储的是经MD5加密之后的密文,而且MD5是不可逆的,所以,需要将这个该用户的盐值取出来,然后和用户明文密码拼接,经过MD5算法再一次加密得到一个密文,将该密文与数据库中这个用户对应的密文进行字符串对比,如果相等,说明用户密码正确,予以登录,这是由于MD5加密算法的唯一性可以做出的判断。
总结:保存salt值是为了下一次登录密码比较使用。

为什么需要setActivationCode,设置激活码?

因为,服务

如果没有激活码的话,人为地构造一个请求也可以进行账号激活,就失去了通过邮件激活的意义,因为用户大可以用一个假邮箱注册,但是通过构造url进行注册。所以,只有激活码这种方式,激活码是一个随机字符串,用户不好认为构造,只能写一个真实的邮箱,然后点击邮件中的激活链接,邮件中的激活链接已经带上了用户id 和 激活码。所以直接点击,然后服务器会获取请求,从数据库对用的用户id取出激活码,判断这两个激活码是否一致。

服务端发送激活邮件

        // 激活邮件Context context = new Context();context.setVariable("email", user.getEmail());// http://localhost:8080/community/activation/101/codeString url = domain + contextPath + "/activation/" + user.getId() + "/" + user.getActivationCode();context.setVariable("url", url);String content = templateEngine.process("/mail/activation", context);mailClient.sendMail(user.getEmail(), "激活账号", content);

在这里插入图片描述
通过上下文对象、模板、模板引擎将动态数据渲染到html页面中,生成视图(html页面),就是字符串。
将字符串设置位邮件的content,然后发送给用户。

controller层 LoginController中添加register方法:

注册失败返回注册页面需要将错误信息现实在注册页面上。

激活注册账号

    public int activation(int userId, String code) {User user = userMapper.selectById(userId);if (user.getStatus() == 1) {return ACTIVATION_REPEAT;} else if (user.getActivationCode().equals(code)) {userMapper.updateStatus(userId, 1);return ACTIVATION_SUCCESS;} else {return ACTIVATION_FAILURE;}}

点击邮件中的链接,访问服务端的激活服务

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/793151.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[每周一更]-第92期:Go项目中的限流算法

这周五在清明假期内&#xff0c;提前更新文章 很多业务会有限流的场景&#xff0c;比如活动秒杀、社区搜索查询、社区留言功能&#xff1b;保护自身系统和下游系统不被巨型流量冲垮等。 在计算机网络中&#xff0c;限流就是控制网络接口发送或接收请求的速率&#xff0c;它可防…

【算法练习】28:选择排序学习笔记

一、选择排序的算法思想 弄懂选择排序算法&#xff0c;先得知道两个概念&#xff1a;未排序序列&#xff0c;已排序序列。 原理&#xff1a;以升序为例&#xff0c;选择排序算法的思想是&#xff0c;先将整个序列当做未排序的序列&#xff0c;以序列的第一个元素开始。然后从左…

K8S - Deployment 的版本回滚

当前状态 先看deployment rootk8s-master:~# kubectl get deploy -o wide --show-labels NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES …

理解pytorch的广播语义

目录 什么是广播运算 广播的条件 示例 示例1 示例2 示例3 补1 示例4 原位运算 示例5 参与广播运算的两个tensor&#xff0c;必须是从右向左对齐 总结规律 两个tensor可以做广播运算的条件&#xff1a; 两个可以互相广播的tensor运算的步骤&#xff1a; 例子&#x…

【深度学习】深度学习md笔记总结第3篇:TensorFlow介绍,学习目标【附代码文档】

深度学习笔记完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;深度学习课程&#xff0c;深度学习介绍要求,目标,学习目标,1.1.1 区别,学习目标,学习目标。TensorFlow介绍&#xff0c;2.4 张量学习目标,2.4.1 张量(Tensor),2.4.2 创建张量的指令,2.4.3 张量…

整数与浮点数在内存中的存储

整数与浮点数在内存中的存储 一&#xff0c;大小端存储二&#xff0c;整数在内存中的存储三&#xff0c;浮点数在内存中的存储3.1浮点数的存储规则3.2浮点数的存储过程3.2.1有效数字M3.2.2指数E3.2.3浮点数存储的特殊情况4&#xff0c;例题讲解 在C语言的编程中&#xff0c;我们…

分布式系统架构中的相关概念

1.1、衡量网站的性能指标 响应时间&#xff1a;指执行一个请求从开始到最后收到响应数据所花费的总体时间。并发数&#xff1a;指系统同时能处理的请求数量。 并发连接数&#xff1a;指的是客户端向服务器发起请求&#xff0c;并建立了TCP连接。每秒钟服务器连接的总TCP数量请…

Python--Django--说明

Django 是基于python 的 Web 开发框架. &nsbp;   Web开发指的是开发基于B/S 架构, 通过前后端的配合, 将后台服务器上的数据在浏览器上展现给前台用户的应用. &nsbp;   在早期, 没有Web框架的时候, 使用 Python CGI 脚本显示数据库中的数据. Web框架致力于解决一些…

c++宏有什么离谱操作?

Boost.Preprocessor确实是一个非常强大而复杂的C宏库&#xff0c;专门用于元编程&#xff0c;即在编译时进行代码生成和变换。我这里有一套编程入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习编程不妨点个关注&#xff0c;给个评论222&…

面试总结------2024/04/04

1.面试官提问&#xff1a;你说你在项目中使用springsecurity jwt 实现了登录功能&#xff0c;能简单讲一下怎么实现的吗&#xff1f; 2.使用RabbitMQ实现订单超时取消功能 订单状态定义 首先&#xff0c;我们需要定义订单的不同状态。在这个示例中&#xff0c;我们可以定义以下…

实验一 Windows 2008虚拟机安装、安装VM Tools、快照和链接克隆、添加硬盘修改格式为GPT

一、安装vmware workstation软件 VMware workstation的安装介质&#xff0c;获取路径&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1AUAw_--yjZAUPbsR7StOJQ 提取码&#xff1a;umz1 所在目录&#xff1a;\vmware\VMware workstation 15.1.0 1.找到百度网盘中vmwa…

Pandas Dataframe合并连接Join和merge 参数讲解

文章目录 函数与参数分析otheronhowlsuffix, rsuffix, suffixesleft_index, right_index 函数与参数分析 在pandas中主要有两个函数可以完成table之间的join Join的函数如下&#xff1a; DataFrame.join(other, onNone, how‘left’, lsuffix‘’, rsuffix‘’, sortFalse, v…

YOLOv8 UI界面设计+热力图显示

进入可视化设计界面&#xff0c;设计UI pyside6-designer 设计好UI保存&#xff0c;然后通过以下命令将ui文件保存为py pyside6-uic myui.ui > myui.py 通过以下命令将资源文件qrc保存为py pyside6-rcc my_rc.qrc > my_rc.py 写主窗口函数实现功能... 项目基于yol…

基于Spring Boot的职称评审管理系统

基于Spring Boot的职称评审管理系统 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 部分系统展示 前台首页界面 用户注册登录界面 管理员登录界面 个人中心界面…

Driver not loaded之记录Qt访问MySql的解决经历

对于这个问题的本质原因&#xff0c;我也搞不明白&#xff0c;所以记录的方法不一定对所有人行之有效。我的目的很简单&#xff0c;就是把数据库用起来&#xff0c;经过查找网上资料&#xff0c;最终把数据库跑起来了。因此记录如下&#xff1a; 1&#xff0c;出现这个问题是缺…

【Go】十六、文件操作

文章目录 1、打开和关闭文件2、IO3、一次性读文件4、带缓冲区的读文件5、写入文件6、文件复制 1、打开和关闭文件 package main import("fmt""os" ) func main(){//打开文件&#xff1a;file,err : os.Open("d:/Test.txt");if err ! nil {//出错…

【医学影像数据处理】nii 数据格式文件操作汇总

大部分医学领域数据存储的都是dicom格式&#xff0c;但是对于CT等一类的序号图像&#xff0c;就需要多个dicom文件独立存储&#xff0c;最终构成一个序列series&#xff0c;这样存储就太过于复杂了。 nifti&#xff08;Neuroimaging Informatics Technology Initiative&#x…

GT收发器64B66B协议(2)自定义PHY设计

文章目录 前言一、设计框图二、GT_module三、PHY_module3.1、PHY_tx模块3.2、PHY_rx_bitsync模块3.3、PHY_rx模块 四、上板测试 前言 有了对64B66B协议的认识以及我们之前设计8B10B自定义PHY的经验&#xff0c;本文开始对64B66B自定义PHY的设计 一、设计框图 二、GT_module …

蓝桥杯单片机速成8-NE555频率测量

一、原理图 NOTE&#xff1a;使用NE555测量频率之前&#xff0c;需要将J3-15(SIGNAL)与J3-16(P34短接) 在使用矩阵键盘的时候也记得把跳冒拔下&#xff0c;因为有公共引脚P34 又是因为他的输出引脚是P34&#xff0c;所以只能用定时器0来作为计数器进行频率测量了 二、代码实现 …

CSS设置网页背景

目录 概述&#xff1a; 1.background-color: 2.background-image&#xff1a; 3.background-repeat&#xff1a; 4.background-position&#xff1a; 5.background-attachment&#xff1a; 6.background-size&#xff1a; 7.background-origin&#xff1a; 8.background-…