个人记录一下觉得有意思的功能模块
目录
发送邮件的功能封装在spring中,可以直接调用,首先导入相应的依赖
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-mailartifactId>
- <version>2.1.5.RELEASEversion>
- dependency>
再修改一下application.properties配置文件,加入以下内容
- # MailProperties
- spring.mail.host=smtp.sina.com
- spring.mail.port=465
- spring.mail.username=你自己的邮箱地址
- spring.mail.password=邮箱的授权码
- spring.mail.protocol=smtps
- spring.mail.properties.mail.smtp.ssl.enable=true
好了,接下来我们开始写一下发送邮件的工具类MailClient
- @Component
- public class MailClient {
-
- 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) {
- 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());
- }
- }
-
- }
接下来我们测试一下这个工具类
- package com.nowcoder.community;
-
- import com.nowcoder.community.util.MailClient;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.test.context.SpringBootTest;
- import org.springframework.test.context.ContextConfiguration;
- import org.springframework.test.context.junit4.SpringRunner;
- import org.thymeleaf.TemplateEngine;
- import org.thymeleaf.context.Context;
-
- @RunWith(SpringRunner.class)
- @SpringBootTest
- @ContextConfiguration(classes = CommunityApplication.class)
- public class MailTests {
-
- @Autowired
- private MailClient mailClient;
-
- @Autowired
- private TemplateEngine templateEngine;
- //发送文字消息
- @Test
- public void testTextMail() {
- mailClient.sendMail("949464367@qq.com", "TEST", "Welcome.");
- }
- //发送网页(需要事先准备好网页资源)
- @Test
- public void testHtmlMail() {
- //Context为网页正文内容
- Context context = new Context();
- //为正文内容的变量进行赋值
- context.setVariable("username", "sunday");
- //静态资源路径 template/context
- String content = templateEngine.process("/mail/demo", context);
- System.out.println(content);
- //发送邮件
- mailClient.sendMail("949464367@qq.com", "HTML", content);
- }
-
- }
网页版测试结果
特别给力,这个方法可以用到后面的账号注册激活功能模块里面
通过注册页面,输入账号,密码,邮箱,点击注册后,工程会往注册邮箱发送一份html,点击html中的链接,以实现用户的激活。
用户的账号,密码的注册,为安全性考虑,不能直接存储进数据库,下场就是学习通,需要进行加密存储。需要编写一个加密工具类,对用户信息进行加密。
采用MD5加密方法,为防止密码过于简单,导致即便是加密后也容易破解的问题,设置了加密盐,也就是在原密码后,生成n位随机字符串,再进行整体的一个加密。
工具类如下所示
- package com.nowcoder.community.util;
-
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.util.DigestUtils;
-
- import java.util.UUID;
-
- public class CommunityUtil {
-
- // 生成随机字符串
- public static String generateUUID() {
- return UUID.randomUUID().toString().replaceAll("-", "");
- }
-
- // MD5加密
- // hello -> abc123def456,这样的弊端就是简单的密码加密之后依旧是容易破解的
- //在原账号的基础上加上随机的字符串,再进行加密,这样不容易被破解
- // hello + 3e4a8 -> abc123def456abc
- public static String md5(String key) {
- //空值不处理
- if (StringUtils.isBlank(key)) {
- return null;
- }
- //spring自带的mp5加密
- return DigestUtils.md5DigestAsHex(key.getBytes());
- }
-
- }
注册用户的账号,邮箱不能重复注册,账号需要激活以后才能使用,账号不能重复激活。
不能重复注册实现方式,即去数据库找是否已存在,账号需要激活,不能重复激活,通过定义一组常量,通过它属性的变化,给user赋予相应的值
- package com.nowcoder.community.util;
-
- public interface CommunityConstant {
-
- /**
- * 激活成功
- */
- int ACTIVATION_SUCCESS = 0;
-
- /**
- * 重复激活
- */
- int ACTIVATION_REPEAT = 1;
-
- /**
- * 激活失败
- */
- int ACTIVATION_FAILURE = 2;
-
- }
user类为如下所示
- package com.nowcoder.community.entity;
-
- import java.util.Date;
-
- public class User {
-
- private int id;
- //用户名
- private String username;
- //密码
- private String password;
- //加密盐
- private String salt;
- //邮箱
- private String email;
- //用户种类
- private int type;
- //用户状态 0为未激活,1为激活
- private int status;
- //用户的激活码
- private String activationCode;
- //用户的头像地址,获取头像
- private String headerUrl;
- //用户的注册时间
- private Date createTime;
-
- public int getId() {
- return id;
- }
-
- public void setId(int id) {
- this.id = id;
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public String getSalt() {
- return salt;
- }
-
- public void setSalt(String salt) {
- this.salt = salt;
- }
-
- public String getEmail() {
- return email;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
-
- public int getType() {
- return type;
- }
-
- public void setType(int type) {
- this.type = type;
- }
-
- public int getStatus() {
- return status;
- }
-
- public void setStatus(int status) {
- this.status = status;
- }
-
- public String getActivationCode() {
- return activationCode;
- }
-
- public void setActivationCode(String activationCode) {
- this.activationCode = activationCode;
- }
-
- public String getHeaderUrl() {
- return headerUrl;
- }
-
- public void setHeaderUrl(String headerUrl) {
- this.headerUrl = headerUrl;
- }
-
- public Date getCreateTime() {
- return createTime;
- }
-
- public void setCreateTime(Date createTime) {
- this.createTime = createTime;
- }
-
- @Override
- public String toString() {
- return "User{" +
- "id=" + id +
- ", username='" + username + '\'' +
- ", password='" + password + '\'' +
- ", salt='" + salt + '\'' +
- ", email='" + email + '\'' +
- ", type=" + type +
- ", status=" + status +
- ", activationCode='" + activationCode + '\'' +
- ", headerUrl='" + headerUrl + '\'' +
- ", createTime=" + createTime +
- '}';
- }
-
- }
注册成功以后,工程会向用户发送激活邮件,点击里面的链接才能成功对用户进行激活。
加入配置项
- # community域名
- community.path.domain=http://localhost:8088
编写service层,实现注册的具体功能,以及邮件收发,UserService
- package com.nowcoder.community.service;
-
- import com.nowcoder.community.dao.UserMapper;
- import com.nowcoder.community.entity.User;
- import com.nowcoder.community.util.CommunityConstant;
- import com.nowcoder.community.util.CommunityUtil;
- import com.nowcoder.community.util.MailClient;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Service;
- import org.thymeleaf.TemplateEngine;
- import org.thymeleaf.context.Context;
-
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Random;
-
- @Service
- public class UserService implements CommunityConstant {
-
- @Autowired
- private UserMapper userMapper;
-
- @Autowired
- private MailClient mailClient;
-
- @Autowired
- private TemplateEngine templateEngine;
- //@Value注解,从配置文件中提取值
- @Value("${community.path.domain}")
- private String domain;
- //@Value注解,从配置文件中提取值
- @Value("${server.servlet.context-path}")
- private String contextPath;
-
- public User findUserById(int id) {
- return userMapper.selectById(id);
- }
-
- public Map
register(User user) { - Map
map = new HashMap<>(); -
- // 空值处理
- 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));
- //原注册密码加上加密盐,再用md5进行加密,这样的密码难以破解
- 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);
-
- // 激活邮件
- Context context = new Context();
- context.setVariable("email", user.getEmail());
- // http://localhost:8080/community/activation/101/code 项目的访问路径,展示在网页上
- String url = domain + contextPath + "/activation/" + user.getId() + "/" + user.getActivationCode();
- context.setVariable("url", url);
- //将正文内容放到模板引擎中,即可跳转访问template文件夹下mail文件夹下的activation.html资源
- String content = templateEngine.process("/mail/activation", context);
- mailClient.sendMail(user.getEmail(), "激活账号", content);
-
- return map;
- }
-
- 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;
- }
- }
-
- }
以及对应的Controller层 LoginController
- package com.nowcoder.community.controller;
-
- import com.nowcoder.community.entity.User;
- import com.nowcoder.community.service.UserService;
- import com.nowcoder.community.util.CommunityConstant;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import org.springframework.ui.Model;
- import org.springframework.web.bind.annotation.PathVariable;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestMethod;
-
- import java.util.Map;
-
- @Controller
- public class LoginController implements CommunityConstant {
-
- @Autowired
- private UserService userService;
-
- @RequestMapping(path = "/register", method = RequestMethod.GET)
- public String getRegisterPage() {
- return "/site/register";
- }
-
- @RequestMapping(path = "/login", method = RequestMethod.GET)
- public String getLoginPage() {
- return "/site/login";
- }
- //注册触发的函数,成功则发送一封邮件
- @RequestMapping(path = "/register", method = RequestMethod.POST)
- public String register(Model model, User user) {
- Map
map = userService.register(user); - //成功
- 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";
- }
- }
- //发送激活邮件,通过点击链接触发的函数,通过读取请求地址里面的参数,判定激活码是否有效,判定用户是否成功激活
- // http://localhost:8080/community/activation/101/code
- @RequestMapping(path = "/activation/{userId}/{code}", method = RequestMethod.GET)
- 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";
- }
-
- }
注册界面为register.html
- html>
- <html lang="en" xmlns:th="http://www.thymeleaf.org">
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
- <link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/>
- <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous">
- <link rel="stylesheet" th:href="@{/css/global.css}" />
- <link rel="stylesheet" th:href="@{/css/login.css}" />
- <title>牛客网-注册title>
- head>
- <body>
- <div class="nk-container">
-
- <header class="bg-dark sticky-top" th:replace="index::header">
- <div class="container">
-
- <nav class="navbar navbar-expand-lg navbar-dark">
-
- <a class="navbar-brand" href="#">a>
- <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
- <span class="navbar-toggler-icon">span>
- button>
-
- <div class="collapse navbar-collapse" id="navbarSupportedContent">
- <ul class="navbar-nav mr-auto">
- <li class="nav-item ml-3 btn-group-vertical">
- <a class="nav-link" href="../index.html">首页a>
- li>
- <li class="nav-item ml-3 btn-group-vertical">
- <a class="nav-link position-relative" href="letter.html">消息<span class="badge badge-danger">12span>a>
- li>
- <li class="nav-item ml-3 btn-group-vertical">
- <a class="nav-link" href="register.html">注册a>
- li>
- <li class="nav-item ml-3 btn-group-vertical">
- <a class="nav-link" href="login.html">登录a>
- li>
- <li class="nav-item ml-3 btn-group-vertical dropdown">
- <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
- <img src="http://images.nowcoder.com/head/1t.png" class="rounded-circle" style="width:30px;"/>
- a>
- <div class="dropdown-menu" aria-labelledby="navbarDropdown">
- <a class="dropdown-item text-center" href="profile.html">个人主页a>
- <a class="dropdown-item text-center" href="setting.html">账号设置a>
- <a class="dropdown-item text-center" href="login.html">退出登录a>
- <div class="dropdown-divider">div>
- <span class="dropdown-item text-center text-secondary">nowcoderspan>
- div>
- li>
- ul>
-
- <form class="form-inline my-2 my-lg-0" action="search.html">
- <input class="form-control mr-sm-2" type="search" aria-label="Search" />
- <button class="btn btn-outline-light my-2 my-sm-0" type="submit">搜索button>
- form>
- div>
- nav>
- div>
- header>
-
-
- <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"
- th:value="${user!=null?user.password:''}"
- 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>
-
-
- <footer class="bg-dark">
- <div class="container">
- <div class="row">
-
- <div class="col-4 qrcode">
- <img src="https://uploadfiles.nowcoder.com/app/app_download.png" class="img-thumbnail" style="width:136px;" />
- div>
-
- <div class="col-8 detail-info">
- <div class="row">
- <div class="col">
- <ul class="nav">
- <li class="nav-item">
- <a class="nav-link text-light" href="#">关于我们a>
- li>
- <li class="nav-item">
- <a class="nav-link text-light" href="#">加入我们a>
- li>
- <li class="nav-item">
- <a class="nav-link text-light" href="#">意见反馈a>
- li>
- <li class="nav-item">
- <a class="nav-link text-light" href="#">企业服务a>
- li>
- <li class="nav-item">
- <a class="nav-link text-light" href="#">联系我们a>
- li>
- <li class="nav-item">
- <a class="nav-link text-light" href="#">免责声明a>
- li>
- <li class="nav-item">
- <a class="nav-link text-light" href="#">友情链接a>
- li>
- ul>
- div>
- div>
- <div class="row">
- <div class="col">
- <ul class="nav btn-group-vertical company-info">
- <li class="nav-item text-white-50">
- 公司地址:北京市朝阳区大屯路东金泉时代3-2708北京牛客科技有限公司
- li>
- <li class="nav-item text-white-50">
- 联系方式:010-60728802(电话) admin@nowcoder.com
- li>
- <li class="nav-item text-white-50">
- 牛客科技©2018 All rights reserved
- li>
- <li class="nav-item text-white-50">
- 京ICP备14055008号-4
- <img src="http://static.nowcoder.com/company/images/res/ghs.png" style="width:18px;" />
- 京公网安备 11010502036488号
- li>
- ul>
- div>
- div>
- div>
- div>
- div>
- footer>
- div>
-
- <script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous">script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" crossorigin="anonymous">script>
- <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" crossorigin="anonymous">script>
- <script th:src="@{/js/global.js}">script>
- <script th:src="@{/js/register.js}">script>
- body>
- html>
激活界面activation.html