QQ邮箱在设置-账户中启动客户端SMTP服务
#MailProperties
spring.mail.host=smtp.qq.com
spring.mail.port=465
spring.mail.username=
spring.mail.password=
spring.mail.protocol=smtps
spring.mail.properties.mail.smtp.ssl.enable=true
封装Mail工具类以便后续复用
@Component
public class MailClient {
//声明一个logger来记录日志,以当前类命名
private static final Logger logger = LoggerFactory.getLogger(MailClient.class);
@Autowired
private JavaMailSender mailSender;
//发件人
@Value("${spring.mail.username}")
private String from;
public void sendMail(String to, String subject, String content){
//构建MineMessage(在JavaMailSender上点击Ctrl键)
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message);
//设置发件人
helper.setFrom(from);
//设置收件人
helper.setTo(to);
//设置主题
helper.setSubject(subject);
//设置内容
helper.setText(content, true);
mailSender.send(helper.getMimeMessage());
} catch (MessagingException e) {
logger.error("发送邮件失败" + e.getMessage());
}
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class MailTests {
@Autowired
private MailClient mailClient;
//主动调用thymeleaf模板引擎,有一个核心类被Spring容器管理
@Autowired
private TemplateEngine templateEngine;
@Test
public void testTextMail(){
mailClient.sendMail("[email protected]","Test","Welcome.");
}
@Test
public void textHtmlMail(){
Context context = new Context();
context.setVariable("username", "sunday");
String content = templateEngine.process("/mail/demo", context);
System.out.println(content);
mailClient.sendMail("[email protected]","HTML", content);
}
}
DOCTYPE html>
<html lang="en" xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>邮件示例title>
head>
<body>
<p>欢迎你,<span style="color:red;" th:text="${username}">span>!p>
body>
html>
//访问注册页面,返回模板路径
@RequestMapping(path = "/register", method = RequestMethod.GET)
public String getRegisterPage(){
return "/site/register";
}
xmlns:th="http://www.thymeleaf.org"
th:href="@{/css/login.css}"
<a class="nav-link" th:href="@{/index}">首页a>
<a class="nav-link" th:href="@{/register}">注册a>
<header class="bg-dark sticky-top" th:fragment="header">
在register.html中header部分添加
<header class="bg-dark sticky-top" th:replace="index::header">
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
<version>3.12.0version>
dependency>
#community
community.path.domain=http://localhost:8080
public class CommunityUtil {
//生成随机字符串,UUID是java里自带的工具
//UUID的字符串通常由字母和横线构成,替换掉-
public static String generateUUID(){
return UUID.randomUUID().toString().replaceAll("-","");
}
//MD5加密
//只能加密不能解密,不安全hello->abc123def456
//在字母后面加上随机字符串(salt)hello +3e4a8->abc123def456abc
//key:原始密码+随机字符串
public static String md5(String key){
//判断参数是否为空,使用工具StringUtils来自包apache.commons.lang3
if(StringUtils.isBlank(key)){
return null;
}
//调用工具Spring自带工具DigestUtils将返回的结果加密成一个16进制的字符串返回,要求参数传入的是byte,将key转换成byte
return DigestUtils.md5DigestAsHex(key.getBytes());
}
}
//注入发送邮件的客户端
@Autowired
private MailClient mailClient;
//注入模板引擎
@Autowired
private TemplateEngine templateEngine;
#community
community.path.domain=http://localhost:8080
项目名:application.properties文件中
#ServerProperties
server.servlet.context-path=/community
//UserSerivce.java
//注入配置文件中域名和项目名
//注入固定的值需要用到@Value
@Value("${community.path.domain}")
private String domain;
@Value("${server.servlet.context-path}")
private String contextPath;
// 编写注册业务,写一个公有的方法返回值封装多个内容,内容包括账号为空等错误信息
// 将返回结果封装到map中
// 注册的时候要传入用户内容,包括账号密码和邮箱,所以传入的参数是user对象
public Map<String, Object> register(User user){
//实例化map
Map<String, Object> map = new HashMap<>();
// 对参数空值判断处理
if(user == null){
throw new IllegalArgumentException("参数不能为空!");
}
// 使用StringUtils工具判断账号为空
// 账号为空是业务上的漏洞,不是程序的错误,把信息封装到map中返回给客户端,而不是抛一场
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为数据库中查到的对象,与传入的参数user相区分
User u = userMapper.selectByName(user.getUsername());
// u!=null 证明存在一个一样的对象
if(u !=null){
map.put("usernameMsg","该账号已存在!");
return map;
}
// 验证邮箱是否已注册
u = userMapper.selectByEmail(user.getEmail());
if(u !=null){
map.put("emailMsg","该邮箱已被注册!");
return map;
}
// 注册用户
// 对密码加密,生成密码随机salt
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());
// 使用format方法设置随机头像路径
user.setHeaderUrl(String.format("https://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));
user.setCreateTime(new Date());
userMapper.insertUser(user);
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}">[email protected]b>, 您好!
p>
<p>
您正在注册牛客网, 这是一封激活邮件, 请点击
<a th:href="${url}">此链接a>,
激活您的牛客账号!
p>
div>
body>
html>
// 激活邮件
// 创建context对象携带变量
Context context = new Context();
context.setVariable("email",user.getEmail());
// 要求访问路径为 http://localhost:8080/community/activation/101(用户id)/code
// 注册用户不包括Id,在实现insert操作时Mybatis实现Id回填
// 原因是在application.properties中使用useGeneratedKeys自动生成Id机制
String url = domain +contextPath +"/activation/" +user.getId() + "/" +user.getActivationCode();
context.setVariable("url",url);
// 利用模板引擎生成邮件内容
String content = templateEngine.process("/mail/activation",context);
// 调用mailClient发送邮件
mailClient.sendMail(user.getEmail(), "激活账号", content);
return map;
// 处理注册请求:浏览器提交数据,方法用post
@RequestMapping(path = "/register", method = RequestMethod.POST)
// model参数存数据携带给模板,还需要传入账号密码邮箱存放在user对象中
public String register(Model model, User user){
Map<String, Object> map = userService.register(user);
// map为空表示注册成功
if(map == null || map.isEmpty()){
// 给中间页面提示消息,跳转到首页
model.addAttribute("msg","注册成功,我们已经向您的邮箱发送了一封激活邮件,请尽快激活!");
model.addAttribute("target","/index");
return "/site/operate-result";
} else {
model.addAttribute("usernameMsg", map.get("usernameMsg"));
model.addAttribute("passwordMsg", map.get("passwordMsg"));
model.addAttribute("emailMsg", map.get("emailMsg"));
return "/site/register";
}
}
<p class="lead" th:text="${msg}">您的账号已经激活成功,可以正常使用了!p>
<hr class="my-4">
<p>
系统会在 <span id="seconds" class="text-danger">8span> 秒后自动跳转,
您也可以点此 <a id="target" th:href="@{${target}}" class="text-primary">链接a>, 手动跳转!
p>
7.继续修改register.html
<div class="main">
<div class="container pl-5 pr-5 pt-3 pb-3 mt-3 mb-3">
<h3 class="text-center text-info border-bottom pb-3">注 册h3>
<form class="mt-5" method="post" th:action="@{/register}">
<div class="form-group row">
<label for="username" class="col-sm-2 col-form-label text-right">账号:label>
<div class="col-sm-10">
<input type="text"
th:class="|form-control ${usernameMsg!=null?'is-invalid':''}|"
th:value="${user!=null?user.username:''}"
id="username" name="username" placeholder="请输入您的账号!" required>
<div class="invalid-feedback" th:text="${usernameMsg}">
该账号已存在!
div>
div>
div>
<div class="form-group row mt-4">
<label for="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':''}|"
th:value="${user!=null?user.password:''}"
id="password" name="password" placeholder="请输入您的密码!" required>
<div class="invalid-feedback" th:text="${passwordMsg}">
密码长度不能小于8位!
div>
div>
div>
<div class="form-group row mt-4">
<label for="confirm-password" class="col-sm-2 col-form-label text-right">确认密码:label>
<div class="col-sm-10">
<input type="password" class="form-control"
id="confirm-password" placeholder="请再次输入密码!" required>
<div class="invalid-feedback">
两次输入的密码不一致!
div>
div>
div>
<div class="form-group row">
<label for="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':''}|"
th:value="${user!=null?user.email:''}"
id="email" name="email" placeholder="请输入您的邮箱!" required>
<div class="invalid-feedback" th:text="${emailMsg}">
该邮箱已注册!
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>
div>
div>
为了实现功能:改变用户的状态
package com.nowcoder.community.util;
public interface CommunityConstant {
/**
* 激活成功
*/
int ACTIVATION_SUCCESS = 0;
/**
* 重复激活
*/
int ACTIVATION_REPEAT = 1;
/**
* 激活失败
*/
int ACTIVATION_FAILURE = 2;
}
// 激活方法,返回激活状态
// 根据激活链接http://localhost:8080/community/activation/101(用户id)/code设置传入参数为id和code
public int activation(int userId, String code){
User user = userMapper.selectById(userId);
// 初始化status为0
if(user.getStatus() == 1){
return ACTIVATION_REPEAT;
}
// 如果激活码和传入激活码是一样的
else if (user.getActivationCode().equals(code)) {
// 将用户status改为1表示激活成功
userMapper.updateStatus(userId,1);
return ACTIVATION_SUCCESS;
} else {
return ACTIVATION_FAILURE;
}
}
// 处理激活请求,路径为http://localhost:8080/community/activation/101/code
@RequestMapping(path = "/action/{userId}/{code}", method = RequestMethod.GET)
// 用model传参
public String activation(Model model, @PathVariable("userId") int userId, @PathVariable("code") String code){
int result = userService.activation(userId,code);
if(result == ACTIVATION_SUCCESS){
model.addAttribute("msg","激活成功,您的账号已经可以正常使用了!");
model.addAttribute("target","/login");
} else if (result == ACTIVATION_REPEAT) {
model.addAttribute("msg","无效操作,该账号已经激活过了!");
model.addAttribute("target","/index");
} else {
model.addAttribute("msg","激活失败,您提供的激活码不正确!");
model.addAttribute("target","/index");
}
return "/site/operate-result";
}
增加访问登录页面的请求
@RequestMapping(path = "/login", method = RequestMethod.GET)
public String getLoginPage(){
return "/site/login";
}