现在很多网站,实现登录或者注册的时候都会用到验证码之类的方式确保安全,Spring Boot,接触Spring Boot没多久,今天完成了Spring Boot 整合邮件服务实现利用邮箱完成注册的功能
这里给一个测试访问地址:180.76.99.142:8080,
所有注释以及全部代码都在下面,方便随时查阅
下面开始从零搭建
1、创建数据库
数据库名:springemail
2、idea创建一个maven工程pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<name>EmailPro</name>
<groupId>site.tian</groupId>
<artifactId>EmailPro</artifactId>
<version>1.0</version>
<!--Spring Boot父依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<dependencies>
<!--邮件服务-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!--web模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!--引入连接池druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
<!--添加log4j不然德鲁伊配置项目无法启动-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--引入自动生成get set方法,还可以使用日志-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--添加jdbc启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--添加数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<!--引入通用mapper的启动器,包括了mybatis,注意:如果之前使用的是mybatis启动器,要在@MapperScan重新导包-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
<!--加入thymeleaf启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--测试的启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
<!--打成jar包-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.工程目录结构
功能虽小但五脏俱全 静态文件下载地址:http://180.76.99.142/jt.zip
4、login页面(其实没必要这么多,纯属好看,登陆注册都在这里面)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Astronauts sign up & login Form a Flat Responsive Widget Template :: xmoban.cn </title>
<!-- Meta tags -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="keywords" content=" Astronauts sign up & login Form Responsive Widget, Audio and Video players, Login Form Web Template, Flat Pricing Tables, Flat Drop-Downs, Sign-Up Web Templates, Flat Web Templates, Login Sign-up Responsive Web Template, Smartphone Compatible Web Template, Free Web Designs for Nokia, Samsung, LG, Sony Ericsson, Motorola Web Design"
/>
<script>
addEventListener("load", function() { setTimeout(hideURLbar, 0); }, false); function hideURLbar(){ window.scrollTo(0,1); }
</script>
<!-- Meta tags -->
<!--pop-ups-->
<link th:href="@{~/css/popuo-box.css}" rel="stylesheet" type="text/css" media="all" />
<!-- //pop-ups-->
<!--stylesheets-->
<link th:href="@{~/css/style.css}" rel='stylesheet' type='text/css' media="all">
<!--//style sheet end here-->
<link href="//fonts.googleapis.com/css?family=Barlow:300,400,500" rel="stylesheet">
</head>
<body>
<h1 class="header-w3ls">
登录 或者~ 注册
</h1>
<div class="art-bothside">
<div class="mid-cls">
<div class="art-right-w3ls">
<h2>Astronauts sign up and login</h2>
<p>consectetur adipiscing elit, sed do eiusmod tempor incididunt Lorem ipsum dolor sit amet</p>
<form id="myForm" th:action="reg" method="post">
<div class="main">
<div class="form-left-to-w3l">
<input type="text" name="username" placeholder="用户名" required="">
</div>
<div class="form-right-w3ls">
<input type="email" name="email" placeholder="注册邮箱~ ~ " required="">
</div>
</div>
<div class="main">
<div class="form-left-to-w3l">
<input type="password" name="password" placeholder="Password" id="password" required="">
<div class="clear"></div>
</div>
<div class="form-right-w3ls ">
<input type="password" placeholder="Confirm Password" id="confirm_password" required="">
</div>
</div>
<div class="btnn">
<button th:id="submit" type="submit">注册</button>
<span class="btn-block" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}" style="color: red"></span>
</div>
</form>
<div class="banner-agileits-btm">
<div class="w3layouts_more-buttn">
<h3>Already have an account..? <a href="#small-dialog1 " class="play-icon popup-with-zoom-anim">login</a></h3>
</div>
<div id="small-dialog1" class="mfp-hide w3ls_small_dialog wthree_pop">
<div class="agileits_modal_body">
<!--login form-->
<div class="letter-w3ls">
<form id="TowForm" action="login" method="post">
<div class="form-left-to-w3l">
<input type="text" name="username" placeholder="Name" required="">
</div>
<div class="form-right-w3ls">
<input type="email" name="email" placeholder="Email" required="">
</div>
<div class="form-right-w3ls ">
<input type="password" name="password" placeholder="Password" required="">
</div>
<div class="btnn">
<button th:id="login" type="submit">登录</button><br>
</div>
</form>
<div class="clear"></div>
</div>
<!--//login form-->
</div>
</div>
</div>
</div>
<div class="art-left-w3ls">
<img th:src="@{~/images/right1.jpg}" class="img-fluid" alt="">
</div>
</div>
</div>
<div class="copy">
<p>©2020 Astronauts sign up & login Form. All Rights Reserved | Design by
</div>
<!--js working-->
<script th:src='@{~/js/jquery-2.2.3.min.js}'></script>
<!--//js working-->
<script>
var password = document.getElementById("password")
, confirm_password = document.getElementById("confirm_password");
function validatePassword(){
if(password.value != confirm_password.value) {
confirm_password.setCustomValidity("Passwords Don't Match");
} else {
confirm_password.setCustomValidity('');
}
}
password.onchange = validatePassword;
confirm_password.onkeyup = validatePassword;
</script>
<!--//scripts-->
<script th:src="@{~/js/jquery.magnific-popup.js}"></script>
<!-- //pop-up-box -->
<script>
$(document).ready(function () {
$('.popup-with-zoom-anim').magnificPopup({
type: 'inline',
fixedContentPos: false,
fixedBgPos: true,
overflowY: 'auto',
closeBtnInside: true,
preloader: false,
midClick: true,
removalDelay: 300,
mainClass: 'my-mfp-zoom-in'
});
});
</script>
<script>
$("#submit").click(function () {
$("#myForm").submit();
});
$("#login").click(function () {
$("#TowForm").submit();
})
</script>
</body>
</html>
4.1 、登陆成功后主页面main其实就是从session中取出用户名
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
</head>
<body>
欢迎您:[[${session.user.username}]]
</body>
</html>
5、yml.xml 文件
#设置默认端口
server:
port: 8080
#设置启动时加载控制器
spring:
mail:
host: smtp.126.com #发送邮件服务器
username: d1376537549@126.com #发送邮件的邮箱地址,这里设置成你们自己的就行
password: ~~xxxxxxxx~~ #客户端授权码,不是邮箱密码,网易的是自己设置的,百度搜索网易授权码操作获取
properties.mail.smtp.port: 465 #465或者994
from: d1376537549@126.com # 发送邮件的地址,和上面username一致
#下面配置有兴趣可以自行查阅
properties.mail.smtp.starttls.enable: true
properties.mail.smtp.starttls.required: true
properties.mail.smtp.ssl.enable: true
default-encoding: utf-8
mvc:
servlet:
load-on-startup: 1
datasource: #配置数据源
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/springemail?characterEncoding=utf8
username: "root"
password: "tiantian" #这里有一个坑,这种方式的配置数据库即使数据库密码正确,若数据库密码是由纯数字组成的,依然会报错
type: com.alibaba.druid.pool.DruidDataSource #指定数据源
thymeleaf: #关闭缓存,防止更改页面不能即使刷新
cache: false
mode: HTML5
encoding: utf-8
#mybatis配置
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: site.tian.pojo
configuration: #驼峰命名法配置
map-underscore-to-camel-case: true
#静态资源的访问要放在资源文件夹中才不会被过滤掉,如:static,Public
6、连接数据库还需要配置数据源
package site.tian.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class DruidConfig {
/*配置数据库*/
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid() {
return new DruidDataSource();
}
}
7、为了防止非授权登录,这里简单用拦截器配置一下
package site.tian.interceptor;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String user = (String)request.getSession().getAttribute("user");
if(user == null){
request.setAttribute("msg","您没有权限,请先登录");
request.getRequestDispatcher("/login").forward(request,response);
return false;
}else {
return true;
}
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
8、把拦截器配置到容器中
package site.tian.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import site.tian.interceptor.LoginInterceptor;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/*添加拦截器,添加到最上面最好*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**")
.excludePathPatterns("/","/checkCode","/reg","/login","/js/**","/css/**","/images/**");
}
@Override
/*设置默认跳转的请求视图*/
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login");
registry.addViewController("/login").setViewName("login");
}
}
9、创建实体类
package site.tian.pojo;
import lombok.Data;
@Data
public class User {
private Long id;
private String username;
private String password;
private String email;
/**
* 状态:0代表未激活,1代表激活
*/
private Integer status;
/**
* 利用UUID生成一段数字,发动到用户邮箱,当用户点击链接时
* 在做一个校验如果用户传来的code跟我们发生的code一致,更改状态为“1”来激活用户
*/
private String code;
}
10、OK上面都是准备工作,下面步入正题
10.1
controller
package site.tian.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import site.tian.email.UUIDUtils;
import site.tian.pojo.User;
import site.tian.service.UserService;
import javax.servlet.http.HttpServletRequest;
@Controller
public class LoginController {
@Autowired
private UserService userService;
/*注册*/
@RequestMapping("/reg")
public String res(User user, Model model) {
user.setStatus(0);
String code = UUIDUtils.getUUID() + UUIDUtils.getUUID();
user.setCode(code);
userService.register(user);
model.addAttribute("msg", "注册成功前往邮箱激活!");
return "login";
}
/**
* 校验邮箱中的code激活账户
* 首先根据激活码code查询用户,之后再把状态修改为"1"
*/
@RequestMapping(value = "/checkCode")
public String checkCode(String code) {
User user = userService.checkCode(code);
System.out.println(user);
//如果用户不等于null,把用户状态修改status=1
if (user != null) {
user.setStatus(1);
//把code验证码清空,已经不需要了
user.setCode("");
System.out.println(user);
userService.updateUserStatus(user);
}
return "login";
}
@RequestMapping("/login")
public String login(User user, HttpServletRequest request) {
if (user.getPassword() != null && user.getEmail() != null && user.getUsername() != null) {
User token= userService.login(user);
request.getSession().setAttribute("user",token);
return "main";
}else {
return "login";
}
}
}
10.2 、配置email主要配置有以下几个类
接口
package site.tian.email;
public interface MailService {
void sendHtmlMail(String to, String subject, String content);
}
实现类
package site.tian.email;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
@Service
public class MailServiceImpl implements MailService {
/*开启日志*/
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private JavaMailSender mailSender;
/**
* 配置文件中我的qq邮箱
*/
@Value("${spring.mail.from}")
private String from;
/**
* 发送HTML邮件
* @param to 收件者
* @param subject 邮件主题
* @param content 文本内容
*/
@Override
public void sendHtmlMail(String to,String subject,String content) {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = null;
try {
helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(subject);
helper.setTo(to);
helper.setText(content, true);
mailSender.send(message);
//日志信息
logger.info("邮件已经发送。");
} catch (MessagingException e) {
logger.error("发送邮件时发生异常!", e);
}
}
}
验证码实现工具类
package site.tian.email;
import java.util.UUID;
public class UUIDUtils {
public static String getUUID(){
return UUID.randomUUID().toString().replace("-","");
}
}
10.3 、业务接口和实现类
接口
package site.tian.service;
import site.tian.pojo.User;
public interface UserService {
public void register(User user);
User checkCode(String code);
void updateUserStatus(User user);
User login(User user);
}
实现类
package site.tian.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import site.tian.email.MailService;
import site.tian.mapper.UserMapper;
import site.tian.pojo.User;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private MailService mailService;
@Override
public void register(User user) {
userMapper.insert(user);
//获取激活码
String code = user.getCode();
System.out.println("code:"+code);
//主题
String subject = "来自天天网站的激活邮件";
//激活码是我们点击邮件链接之后根据激活码查询用户,如果存在说明一致,将用户状态修改为“1”激活,有用户来访问服务器
//上面的激活码发送到用户注册邮箱
String context = "<a href='http://127.0.0.1:8080/checkCode?code="+code+"'>激活请点击:"+code+"进行激活您的账号</a>";
//发送激活邮件
mailService.sendHtmlMail(user.getEmail(),subject,context);
}
@Override
public User checkCode(String code) {
return userMapper.checkCode(code);
}
@Override
public void updateUserStatus(User user) {
userMapper.updateUserStatus(user);
}
@Override
public User login(User user) {
return userMapper.login(user);
}
}
10.4、mapper文件及对应xml
接口
package site.tian.mapper;
import site.tian.pojo.User;
public interface UserMapper {
void insert(User user);
User checkCode(String code);
void updateUserStatus(User user);
User login(User user);
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="site.tian.mapper.UserMapper">
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (username,password,status,code,email) VALUES(#{username},#{password},#{status},#{code},#{email}) ;
</insert>
<update id="updateUserStatus">
UPDATE user SET status = #{status},code=#{code} WHERE id = #{id} ;
</update>
<select id="checkCode" resultType="site.tian.pojo.User">
select * from user where code=#{code}
</select>
<select id="login" resultType="site.tian.pojo.User">
select * from user where status=1 and username=#{username} and email=#{email} and password=#{password}
</select>
</mapper>