文章目录
- SpringBoot+网易邮箱登录注册
- pom.xml
- application.yml
- sql
- UserEmail.java
- UserEmailMapper.java
- UserEmailMapper.xml
- EmailService.java
- UserEmailService.java
- UserEmailServiceImpl.java
- UserEmailController.java
- register1.html
编写前参考
SpringBoot+网易邮箱登录注册
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.sin</groupId>
<artifactId>RedisMailMessage</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>RedisMailMessage</name>
<description>RedisMailMessage</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!--
邮箱业务:
用于操作邮件功能的
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--
对SpringBoot应用的监控
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--
SpringBoot框架的启动器
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--
SpringBoot框架的测试库
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Java提供的工具库 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--
SpringBoot应用打包为可执行的jar文件
-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--
单元测试的插件
配置为逃过运行测试
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.yml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/demo
username: root
password: 123456
redis:
# 地址
host: 192.168.226.137
# 端口
port: 6379
# 密码
password: 123456
# 超时时间 5000毫秒
timeout: 5000
jedis:
# 连接池
pool:
# 连接池最小空闲连接
min-idle: 0
# 连接池的最大空闲连接
max-idle: 8
# 连接池最大阻塞等待时间(使用负数表示没有限制)
max-wait: -1
# 连接池最大连接数(使用负数表示没有限制)
max-active: 8
mail:
# 配置SMTP服务器地址
host: smtp.126.com
# 配置端口号465或者25
port: 25
# 发送者邮箱(根据自己邮箱填写)
username: sin8023@126.com
# 配置密码,注意是开启POP3/SMTP的授权密码(根据自己的授权密码填写)
password: RVJJ****NTPUEHO
properties:
mail:
smtp:
auth: true
enable: true
ssl:
# 设为true时 端口号设为 465 设为false时 端口号设为25
enable: false
socketFactoryClass: javax.net.ssl.SSLSocketFactory
#表示开启 DEBUG 模式,这样,邮件发送过程的日志会在控制台打印出来,方便排查错误
debug: true
# 邮箱编码格式
default-encoding: UTF-8
# thymeleaf:
# prefix: classpath:/templates/
# suffix: .html
# mode: HTML
# encoding: utf-8
# servlet.content-type: text/html
# check-template-location: true #检查模板路径是否存在
# cache: false
# mybatis配置
mybatis:
# 获取配置文件的地址
mapper-locations: classpath:/mapper/*.xml
# 获取实体类的地址
type-aliases-package: com.sin.pojo
configuration:
# 开启驼峰命名
map-underscore-to-camel-case: true
logging:
level:
org.mybatis: debug
sql
create table user_email
(
id int auto_increment primary key comment '主键id',
name varchar(20) character set utf8mb4 not null comment '用户名',
password varchar(20) character set utf8mb4 not null comment '密码',
email varchar(20) character set utf8mb4 not null comment '邮箱',
code_status int default 0 comment '邮箱验证状态 1 = 未注册 ,1 = 已经注册 其余数字为无效标识符'
)charset = utf8mb4;
insert into user_email(name,password,email,code_status) select email,'aaa' from user where email='123@qq.com';
insert into user_email(name,password,email,code_status) value ('admin','123456','123@qq.com',1);
select * from user_email where name = 'admin' and password = '123456' and code_status = 1;
UserEmail.java
package com.sin.pojo;
public class UserEmail {
private long id;
private String name;
private String password;
private String email;
private int codeStatus;
public UserEmail() {
}
public UserEmail(String name, String password, String email) {
this.name = name;
this.password = password;
this.email = email;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getCode_status() {
return codeStatus;
}
public void setCode_status(int codeStatus) {
this.codeStatus = codeStatus;
}
@Override
public String toString() {
return "UserEmail{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", codeStatus='" + codeStatus + '\'' +
'}';
}
}
UserEmailMapper.java
package com.sin.mapper;
import com.sin.pojo.UserEmail;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* @createTime 2023/10/23 15:39
* @createAuthor SIN
* @use 数据访问层,数据访问的核心任务
*/
@Mapper
public interface UserEmailMapper {
/**
* 添加数据
* @param userEmail
* @return
*/
int insertUserEmail(UserEmail userEmail);
/**
* 根据用户名进行查找数据
* @param userEmail
* @return
*/
UserEmail findUserByName(String userEmail);
/**
* 查询所有数据
* @return
*/
List<UserEmail> seletctUserEmail();
/**
* 根据邮箱进行查询
* @param email
* @return
*/
UserEmail findAllByEmail(String email);
/**
* 更新数据
* @param email
* @return
*/
void updateCodeStatus(String email);
/**
* 根据邮箱查询数据
* @param email
* @return
*/
UserEmail findByEmail(String email);
}
UserEmailMapper.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="com.sin.mapper.UserEmailMapper">
<!--
resultMap通过将查询结果集中的列名和Java对象中的属性名进行映射,实现了结果集的自动映射。
同时,它还可以解决列名和属性名不匹配的问题,支持复杂类型映射,提高了查询性能。
-->
<resultMap id="UserMap" type="com.sin.pojo.UserEmail">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="password" column="password"/>
<result property="email" column="email"/>
<result property="codeStatus" column="code_status"/>
</resultMap>
<!-- 添加数据 -->
<insert id="insertUserEmail" parameterType="com.sin.pojo.UserEmail">
insert into user_email(name,password,email,code_status)
values (#{name},#{password},#{email},#{codeStatus})
</insert>
<update id="updateCodeStatus" parameterType="com.sin.pojo.UserEmail">
update user_email set code_status = 2 where email = #{email};
</update>
<!-- 根据姓名密码查询数据 -->
<select id="findUserByName" resultMap="UserMap">
select name from user where name = #{name} and password = #{password} and code_status = 1
</select>
<!-- 查询所有数据 -->
<select id="seletctUserEmail" resultMap="UserMap">
select * from user_email
</select>
<!-- code_status = 1时,表明用户已经存在但未完成注册操作 -->
<select id="findAllByEmail" resultMap="UserMap">
select * from user_email where email = #{email} and code_status = 1
</select>
<!-- code_status = 2时,表明用户已经完成注册操作可以进行登录操作 -->
<select id="findByEmail" parameterType="string" resultMap="UserMap">
select * from user_email where email = #{email} and code_status = 2
</select>
</mapper>
EmailService.java
package com.sin.service;
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;
/**
* @createTime 2023/11/21 10:18
* @createAuthor SIN
* @use
*/
@Service
public class EmailService {
@Autowired(required = false)
private JavaMailSender mailSender;
@Value("${spring.mail.username}")
private String userName;
/**
* 发送信息邮件
* @param to 收件人
* @param subject 邮箱标题
* @param content 邮箱内容
*/
public void sendMail(String to, String subject, String content) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
//邮箱发送者
helper.setFrom(userName);
//收件人,可以为多个收件人,收件人之间用逗号隔开
helper.setTo(to);
// 邮箱标题
helper.setSubject(subject);
// 邮箱内容
helper.setText(content, true);
mailSender.send(message);
}
}
UserEmailService.java
package com.sin.service;
import com.sin.exception.EmailAlreadyExistsException;
import com.sin.pojo.UserEmail;
import javax.mail.MessagingException;
import java.util.List;
/**
* @createTime 2023/11/20 17:25
* @createAuthor SIN
* @use
*/
public interface UserEmailService {
/**
* 添加数据
* @param userEmail
* @return
*/
int insertUserEmail(UserEmail userEmail) throws MessagingException, EmailAlreadyExistsException;
/**
* 根据用户名进行查找数据
* @param userEmail
* @return
*/
UserEmail findUserByName(String userEmail);
/**
* 查询所有数据
* @return
*/
List<UserEmail> seletctUserEmail();
/**
* 根据邮箱进行查询
* @param email
* @return
*/
UserEmail findAllByEmail(String email);
/**
* 发送验证码
* @return
*/
String sendCode(String email) throws MessagingException;
/**
* 获取验证码
* @return
*/
Object getCode(String email);
/**
* 更新数据
* @param email
* @return
*/
void updateCodeStatus(String email);
/**
* 根据邮箱查询数据
* @return
*/
boolean loginUser(String email, String password);
}
UserEmailServiceImpl.java
package com.sin.service.impl;
import com.sin.mapper.UserEmailMapper;
import com.sin.pojo.UserEmail;
import com.sin.exception.EmailAlreadyExistsException;
import com.sin.service.EmailService;
import com.sin.service.UserEmailService;
import com.sin.utils.RandomCodeUtil;
import com.sin.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.mail.MessagingException;
import java.util.List;
/**
* @createTime 2023/11/20 17:25
* @createAuthor SIN
* @use
*/
@Service
public class UserEmailServiceImpl implements UserEmailService {
@Autowired
private UserEmailMapper userEmailMapper;
@Autowired
private EmailService emailService;
@Override
public int insertUserEmail(UserEmail userEmail) throws MessagingException, EmailAlreadyExistsException {
// 1,查询要添加的数据如果没有数据就添加
UserEmail allByEmail = this.findAllByEmail(userEmail.getEmail());
if (allByEmail != null){
throw new EmailAlreadyExistsException("该邮箱以注册,请登录");
}else {
System.out.println("数据添加成功");
userEmail.setCode_status(1);
emailService.sendMail(userEmail.getEmail(),"验证码","<a href=\"http://localhost:8080/update/"+userEmail.getEmail()+"\">激活请点击:</a>");
return userEmailMapper.insertUserEmail(userEmail);
}
}
@Override
public UserEmail findAllByEmail(String email) {
return userEmailMapper.findAllByEmail(email);
}
@Override
public String sendCode(String email) throws MessagingException {
UserEmail allByEmail = this.findAllByEmail(new UserEmail().getEmail());
return "";
}
@Override
public Object getCode(String email) {
if (email != null){
Object o = redisUtil.get(email);
System.out.println(o);
return o;
}else {
return "未填写邮箱";
}
}
@Override
public void updateCodeStatus(String email) {
userEmailMapper.updateCodeStatus(email);
}
@Override
public boolean loginUser(String email, String password) {
UserEmail userByName = userEmailMapper.findByEmail(email);
return userByName != null && userByName.getPassword().equals(password);
}
@Override
public UserEmail findUserByName(String userEmail) {
return null;
}
@Override
public List<UserEmail> seletctUserEmail() {
return userEmailMapper.seletctUserEmail();
}
}
UserEmailController.java
package com.sin.controller;
import com.sin.exception.EmailAlreadyExistsException;
import com.sin.mapper.UserEmailMapper;
import com.sin.pojo.UserEmail;
import com.sin.service.UserEmailService;
import org.apache.catalina.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.mail.MessagingException;
/**
* @createTime 2023/11/22 11:47
* @createAuthor SIN
* @use
*/
@Controller
public class UserEmailController {
@Autowired
private UserEmailService userEmailService;
@GetMapping("/register")
public String login(Model model){
UserEmail userEmail = new UserEmail();
model.addAttribute("userEmail",userEmail);
// 设置成功消息
model.addAttribute("successMessage", "邮件已发送,请注意查收");
return "register1";
}
@PostMapping("/register")
// @ModelAttribut : 将请求参数绑定到模型对象上,
public String login(@ModelAttribute("userEmail") UserEmail userEmail, Model model) throws MessagingException {
try{
int result = userEmailService.insertUserEmail(userEmail); // 执行用户注册
System.out.println(result);
}catch (EmailAlreadyExistsException e){
String errorMessage = e.getMessage();
System.out.println(errorMessage);
model.addAttribute("errorMessage",errorMessage);
}
return "redirect:/register";
}
@GetMapping("/update/{email}")
@ResponseBody
public String updateCodeStatus(@PathVariable("email") String email){
userEmailService.updateCodeStatus(email);
return "SUCCESS";
}
@PostMapping("/login")
public String loginUser(@RequestParam("email") String email, @RequestParam("password") String password) {
boolean isValid = userEmailService.loginUser(email, password);
System.out.println(isValid);
// 登录成功后返回到/dashboard页面
if (isValid) {
return "SUCCESS";
} else { // 登录失败返回该页面
return "login";
}
}
}
register1.html
<!DOCTYPE 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.0">
<title>登录/注册</title>
<style>
:root {
/* COLORS */
--white: #e9e9e9;
--gray: #333;
--blue: #0367a6;
--lightblue: #008997;
/* RADII */
--button-radius: 0.7rem;
/* SIZES */
--max-width: 758px;
--max-height: 420px;
font-size: 16px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}
body {
align-items: center;
background-color: var(--white);
background: url("https://res.cloudinary.com/dbhnlktrv/image/upload/v1599997626/background_oeuhe7.jpg");
/* 决定背景图像的位置是在视口内固定,或者随着包含它的区块滚动。 */
/* https://developer.mozilla.org/zh-CN/docs/Web/CSS/background-attachment */
background-attachment: fixed;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
display: grid;
height: 100vh;
place-items: center;
}
.form__title {
font-weight: 300;
margin: 0;
margin-bottom: 1.25rem;
}
.link {
color: var(--gray);
font-size: 0.9rem;
margin: 1.5rem 0;
text-decoration: none;
}
.container {
background-color: var(--white);
border-radius: var(--button-radius);
box-shadow: 0 0.9rem 1.7rem rgba(0, 0, 0, 0.25),
0 0.7rem 0.7rem rgba(0, 0, 0, 0.22);
height: var(--max-height);
max-width: var(--max-width);
overflow: hidden;
position: relative;
width: 100%;
}
.container__form {
height: 100%;
position: absolute;
top: 0;
transition: all 0.6s ease-in-out;
}
.container--signin {
left: 0;
width: 50%;
z-index: 2;
}
.container.right-panel-active .container--signin {
transform: translateX(100%);
}
.container--signup {
left: 0;
opacity: 0;
width: 50%;
z-index: 1;
}
.container.right-panel-active .container--signup {
animation: show 0.6s;
opacity: 1;
transform: translateX(100%);
z-index: 5;
}
.container__overlay {
height: 100%;
left: 50%;
overflow: hidden;
position: absolute;
top: 0;
transition: transform 0.6s ease-in-out;
width: 50%;
z-index: 100;
}
.container.right-panel-active .container__overlay {
transform: translateX(-100%);
}
.overlay {
background-color: var(--lightblue);
background: url("https://cdn.pixabay.com/photo/2018/08/14/13/23/ocean-3605547_1280.jpg");
background-attachment: fixed;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
height: 100%;
left: -100%;
position: relative;
transform: translateX(0);
transition: transform 0.6s ease-in-out;
width: 200%;
}
.container.right-panel-active .overlay {
transform: translateX(50%);
}
.overlay__panel {
align-items: center;
display: flex;
flex-direction: column;
height: 100%;
justify-content: center;
position: absolute;
text-align: center;
top: 0;
transform: translateX(0);
transition: transform 0.6s ease-in-out;
width: 50%;
}
.overlay--left {
transform: translateX(-20%);
}
.container.right-panel-active .overlay--left {
transform: translateX(0);
}
.overlay--right {
right: 0;
transform: translateX(0);
}
.container.right-panel-active .overlay--right {
transform: translateX(20%);
}
.btn {
background-color: var(--blue);
background-image: linear-gradient(90deg, var(--blue) 0%, var(--lightblue) 74%);
border-radius: 20px;
border: 1px solid var(--blue);
color: var(--white);
cursor: pointer;
font-size: 0.8rem;
font-weight: bold;
letter-spacing: 0.1rem;
padding: 0.9rem 4rem;
text-transform: uppercase;
transition: transform 80ms ease-in;
}
.form>.btn {
margin-top: 1.5rem;
}
.btn:active {
transform: scale(0.95);
}
.btn:focus {
outline: none;
}
.form {
background-color: var(--white);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 0 3rem;
height: 100%;
text-align: center;
}
.input {
background-color: #fff;
border: none;
padding: 0.9rem 0.9rem;
margin: 0.5rem 0;
width: 100%;
}
@keyframes show {
0%,
49.99% {
opacity: 0;
z-index: 1;
}
50%,
100% {
opacity: 1;
z-index: 5;
}
}
.error-message{
color: red;
}
</style>
</head>
<body>
<div class="container right-panel-active">
<!-- 注册 -->
<div class="container__form container--signup">
<form method="post" th:action="@{/register}" th:object="${userEmail}" class="form" id="form1">
<h2 class="form__title">注册</h2>
<input type="text" placeholder="用户" th:field="*{name}" class="input" />
<input type="password" placeholder="密码" th:field="*{password}" class="input" />
<input type="email" placeholder="邮箱" th:field="*{email}" class="input" />
<div class="error-message" th:if="${errorMessage}"> <!-- 修改变量名为 "errorMessage" -->
<p id = "aaa" th:text="${errorMessage}"></p>
</div>
<button class="btn" type="submit">注册</button>
</form>
</div>
<div class="success-message" th:if="${successMessage}">
<p th:text="${successMessage}"></p>
</div>
<!-- 登录 -->
<div class="container__form container--signin">
<form method="post" th:action="@{/login}" class="form" id="form2">
<h2 class="form__title">登录</h2>
<input type="email" placeholder="Email" name="email" class="input" />
<input type="password" placeholder="Password" name="password" class="input" />
<a href="#" class="link">忘记密码了?</a>
<button class="btn" type="submit" >注册</button>
</form>
</div>
<!-- Overlay -->
<div class="container__overlay">
<div class="overlay">
<div class="overlay__panel overlay--left">
<button class="btn" id="signIn">登录</button>
</div>
<div class="overlay__panel overlay--right">
<button class="btn" id="signUp">注册</button>
</div>
</div>
</div>
</div>
<script>
const signInBtn = document.getElementById("signIn");
const signUpBtn = document.getElementById("signUp");
const container = document.querySelector(".container");
signInBtn.addEventListener("click", () => {
container.classList.remove("right-panel-active");
});
signUpBtn.addEventListener("click", () => {
container.classList.add("right-panel-active");
});
// 获取页面元素
const form = document.getElementById("form1");
const errorMessage = document.getElementById("errorMessage");
// 提交表单时,禁用按钮并显示加载状态
form.addEventListener("submit", function() {
const button = form.querySelector("button[type=submit]");
button.disabled = true;
button.innerText = "加载中...";
});
// 如果页面存在错误消息,则弹出消息框
if (errorMessage) {
alert(errorMessage.innerText);
}
</script>
</body>
</html>
邮箱