/*
Navicat Premium Data Transfer
Source Server : mysql
Source Server Type : MySQL
Source Server Version : 50731
Source Host : localhost:3306
Source Schema : xiaomu
Target Server Type : MySQL
Target Server Version : 50731
File Encoding : 65001
Date: 24/03/2022 07:41:31
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
`salt` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '盐',
`confirm_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '确认码',
`activation_time` datetime(0) NULL DEFAULT NULL COMMENT '激活失效时间',
`is_valid` tinyint(1) NULL DEFAULT NULL COMMENT '是否可用,0不可用,1可用',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
源码链接
可单独添加依赖
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.6.3version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-mailartifactId>
dependency>
完整依赖
<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.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>springboot-login-registryartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>springboot-login-registryname>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASEspring-boot.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-mailartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.4version>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.6.3version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>${spring-boot.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.1version>
<configuration>
<source>1.8source>
<target>1.8target>
<encoding>UTF-8encoding>
configuration>
plugin>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<version>2.3.7.RELEASEversion>
<configuration>
<mainClass>com.example.SpringbootLoginRegistryApplicationmainClass>
configuration>
<executions>
<execution>
<id>repackageid>
<goals>
<goal>repackagegoal>
goals>
execution>
executions>
plugin>
plugins>
build>
project>
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/xiaomu?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
mail:
protocol: smtps # 邮件协议
host: smtp.qq.com # QQ邮箱 smtp 服务器地址
port: 465 # 端口
username: # 邮箱用户名
password: # 授权码
default-encoding: utf-8 # 编码字符集
properties:
mail:
debug: ture # 开启debug模式以后会完整打印邮件发送过程的日志
mybatis:
configuration:
map-underscore-to-camel-case: true # 开启驼峰功能
SystemController
package com.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class SystemController {
/**
* 登录页面
*/
@GetMapping("login")
public String login() {
return "login";
}
/**
* 注册页面
* @return
*/
@GetMapping("registry")
public String registry() {
return "registry";
}
}
UserController
package com.example.controller;
import com.example.pojo.User;
import com.example.service.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Map;
@RestController
@RequestMapping("user")
public class UserController {
@Resource
private UserService userService;
/**
* 注册账号
* @param user
* @return
*/
@PostMapping("create")
public Map<String, Object> createAccount(User user) {
return userService.createAccount(user);
}
/**
* 登录账号
* @param user
* @return
*/
@PostMapping("login")
public Map<String, Object> loginAccount(User user) {
return userService.loginAccount(user);
}
/**
* 激活账号
* @param confirmCode
* @return
*/
@GetMapping("activation")
public Map<String, Object> activationAccount(String confirmCode) {
return userService.activationAccount(confirmCode);
}
}
UserMapper
package com.example.mapper;
import com.example.pojo.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
public interface UserMapper {
/**
* 新增用户
* @param user
* @return
*/
@Insert("INSERT INTO user ( email, password, salt, confirm_code, activation_time, is_valid)" +
"VALUES ( #{email}, #{password}, #{salt}, #{confirmCode}, #{activationTime}, #{isValid})")
int insertUser(User user);
/**
* 根据确认码查询用户
* @param confirmCode
* @return
*/
@Select("SELECT email, activation_time FROM user WHERE confirm_code = #{confirmCode} AND is_valid = 0")
User selectUserByConfirmCode(@Param("confirmCode") String confirmCode);
/**
* 根据确认码查询用户并修改状态码值为 1 (可用)
* @param confirmCode
* @return
*/
@Update("UPDATE user SET is_valid = 1 WHERE confirm_code = #{confirmCode}")
int updateUserByConfirmCode(@Param("confirmCode") String confirmCode);
/**
* 根据邮箱查询用户
* @param email
* @return
*/
@Select("SELECT email, password, salt FROM user WHERE email = #{email} AND is_valid = 1")
List<User> selectUserByEmail(@Param("email") String email);
}
User
package com.example.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private Integer id; // 主键
private String email; // 邮箱
private String password; // 密码,使用md5 + 盐 加密
private String salt; // 盐
private String confirmCode; // 确认码
private LocalDateTime activationTime; // 激活失效时间
private Byte isValid; // 是否可用
}
package com.example.service;
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 org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.Date;
@Service
public class MailService {
@Value("${spring.mail.username}")
private String mailUsername;
@Resource
private TemplateEngine templateEngine;
@Resource
private JavaMailSender javaMailSender;
/**
* 激活账号邮件发送
* @param activationUrl 激活 url 链接
* @param email 收件人的邮箱
*/
public void sendMailForActivationAccount(String activationUrl, String email) {
// 创建邮件对象
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
try {
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true);
// 设置邮件主题
messageHelper.setSubject("欢迎加入小沐 -- 个人账号激活");
// 设置邮件发送者
messageHelper.setFrom(mailUsername);
// 设置邮件加接收者,可以多人
messageHelper.setTo(email);
// 邮件发送日期
messageHelper.setSentDate(new Date());
// 创建上下文环境
Context context = new Context();
context.setVariable("activationUrl", activationUrl);
// 邮件模板
String text = templateEngine.process("activation-account.html", context);
// 设置邮件正文
messageHelper.setText(text, true);
} catch (MessagingException e) {
e.printStackTrace();
}
// 邮件发送
javaMailSender.send(mimeMessage);
}
}
UserService
package com.example.service;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import com.example.mapper.UserMapper;
import com.example.pojo.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class UserService {
@Resource
private UserMapper userMapper;
@Resource
private MailService mailService;
/**
* 注册账号
* @param user
* @return
*/
@Transactional
public Map<String, Object> createAccount(User user){
// 雪花算法
String confirmCode = IdUtil.getSnowflake(1, 1).nextIdStr();
// 盐
String salt = RandomUtil.randomString(6);
// 加密密码:原始密码 + 盐
String md5Pwd = SecureUtil.md5(user.getPassword() + salt);
// 激活失效时间: 24 小时
LocalDateTime ldt = LocalDateTime.now().plusDays(1);
// 初始账号信息
user.setSalt(salt);
user.setPassword(md5Pwd);
user.setConfirmCode(confirmCode);
user.setActivationTime(ldt);
user.setIsValid((byte) 0);
// 新增账号
int result = userMapper.insertUser(user);
Map<String, Object> resultMap = new HashMap<>();
if (result > 0) {
// 发送邮件
String activationUrl = "http://localhost:8080/user/activation?confirmCode=" + confirmCode;
mailService.sendMailForActivationAccount(activationUrl, user.getEmail());
resultMap.put("code", 200);
resultMap.put("message", "注册成功,请前往邮箱进行账号激活");
} else {
resultMap.put("code", 400);
resultMap.put("message", "注册失败");
}
return resultMap;
}
public Map<String, Object> loginAccount(User user) {
Map<String, Object> resultMap = new HashMap<>();
// 根据邮箱查询用户
List<User> userList = userMapper.selectUserByEmail(user.getEmail());
// 查询不到结果,返回:该账号不存在或者未激活
if (userList == null || userList.isEmpty()) {
resultMap.put("code", 400);
resultMap.put("message", "该账号不存在或者未激活");
return resultMap;
}
// 查询到多个用户,返回: 账号异常,请联系管理员
if (userList.size() > 1) {
resultMap.put("code", 400);
resultMap.put("message", "账号异常,请联系管理员");
return resultMap;
}
// 查询到一个用户,进行密码对比
User u = userList.get(0);
// 用户输入的密码和盐进行加密
String md5Pwd = SecureUtil.md5(user.getPassword() + u.getSalt());
// 密码不一致,返回:用户名或者密码错误
if (!u.getPassword().equals(md5Pwd)) {
resultMap.put("code", 400);
resultMap.put("message", "用户名或者密码错误");
return resultMap;
}
resultMap.put("code", 200);
resultMap.put("message", "登录成功");
return resultMap;
}
/**
* 激活账号
* @param confirmCode
* @return
*/
@Transactional
public Map<String, Object> activationAccount(String confirmCode) {
Map<String, Object> resultMap = new HashMap<>();
// 根据用户确认码查询用户
User user = userMapper.selectUserByConfirmCode(confirmCode);
// 判断激活时间是否超时
boolean after = LocalDateTime.now().isAfter(user.getActivationTime());
if (after) {
resultMap.put("code", 400);
resultMap.put("message", "链接已失效,请重新注册");
return resultMap;
}
// 根据确认码查询用户并修改状态值为 1 (可用)
int result = userMapper.updateUserByConfirmCode(confirmCode);
if (result > 0) {
resultMap.put("code", 200);
resultMap.put("message", "激活成功");
return resultMap;
} else {
resultMap.put("code", 400);
resultMap.put("message", "激活失败");
}
return resultMap;
}
}
package com.example;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.example.mapper")
@SpringBootApplication
public class SpringbootLoginRegistryApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootLoginRegistryApplication.class, args);
}
}
activation-acount(简陋模板)
<html>
<body>
<div>
<h1>小沐邮件认证h1>
<h3>账号激活说明h3>
点击下面链接进行账号激活:<br>
<a th:href="@{${activationUrl}}"><span th:text="${activationUrl}">span>a>
div>
body>
html>
login
DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<title>Login Example - Semantictitle>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/reset.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/site.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/container.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/grid.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/header.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/image.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/menu.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/divider.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/segment.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/form.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/input.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/button.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/list.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/message.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/icon.css">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js">script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/form.js">script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/transition.js">script>
<style type="text/css">
body {
background-color: #DADADA;
}
body > .grid {
height: 100%;
}
.image {
margin-top: -100px;
}
.column {
max-width: 450px;
}
style>
<script>
$(document)
.ready(function() {
$('.ui.form')
.form({
fields: {
email: {
identifier : 'email',
rules: [
{
type : 'empty',
prompt : 'Please enter your e-mail'
},
{
type : 'email',
prompt : 'Please enter a valid e-mail'
}
]
},
password: {
identifier : 'password',
rules: [
{
type : 'empty',
prompt : 'Please enter your password'
},
{
type : 'length[6]',
prompt : 'Your password must be at least 6 characters'
}
]
}
}
})
;
})
;
script>
head>
<body>
<div class="ui middle aligned center aligned grid">
<div class="column">
<h2 class="ui teal image header">
<div class="content">
<h2>登 录h2>
div>
h2>
<form class="ui large form" onsubmit="return false;">
<div class="ui stacked segment">
<div class="field">
<div class="ui left icon input">
<i class="user icon">i>
<input type="text" name="email" id="email" placeholder="E-mail address">
div>
div>
<div class="field">
<div class="ui left icon input">
<i class="lock icon">i>
<input type="password" id="password" name="password" placeholder="Password">
div>
div>
<div class="ui fluid large teal submit button" id="login">Logindiv>
div>
<div class="ui error message">div>
form>
<div class="ui message">
New to us? <a href="/registry">Registrya>
div>
div>
div>
<script>
<!-- 登录账号-->
$("#login").on("click", function () {
$.ajax({
url: "/user/login",
type: "POST",
data: {
email: $("#email").val(),
password: $("#password").val(),
},
resultType: "JSON",
success: function (result) {
alert(result.message)
if (200 == result.code) {
window.location.href = "https://www.baidu.com/"; // 登录成功后跳转到百度页面
}
},
error: function (result) {
result
}
})
});
script>
body>
html>
registry
DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<title>Login Example - Semantictitle>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/reset.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/site.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/container.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/grid.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/header.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/image.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/menu.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/divider.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/segment.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/form.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/input.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/button.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/list.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/message.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/icon.css">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js">script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/form.js">script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/components/transition.js">script>
<style type="text/css">
body {
background-color: #DADADA;
}
body > .grid {
height: 100%;
}
.image {
margin-top: -100px;
}
.column {
max-width: 450px;
}
style>
<script>
$(document)
.ready(function() {
$('.ui.form')
.form({
fields: {
email: {
identifier : 'email',
rules: [
{
type : 'empty',
prompt : 'Please enter your e-mail'
},
{
type : 'email',
prompt : 'Please enter a valid e-mail'
}
]
},
password: {
identifier : 'password',
rules: [
{
type : 'empty',
prompt : 'Please enter your password'
},
{
type : 'length[6]',
prompt : 'Your password must be at least 6 characters'
}
]
}
}
})
;
})
;
script>
head>
<body>
<div class="ui middle aligned center aligned grid">
<div class="column">
<h2 class="ui teal image header">
<div class="content">
<h2>注 册h2>
div>
h2>
<form class="ui large form" onsubmit="return false;">
<div class="ui stacked segment">
<div class="field">
<div class="ui left icon input">
<i class="user icon">i>
<input type="text" name="email" id="email" placeholder="E-mail address">
div>
div>
<div class="field">
<div class="ui left icon input">
<i class="lock icon">i>
<input type="password" id="password" name="password" placeholder="Password">
div>
div>
<div class="field">
<div class="ui left icon input">
<i class="lock icon">i>
<input type="password" name="repassword" placeholder="Password">
div>
div>
<div class="ui fluid large teal submit button" id="registry">registrydiv>
div>
<div class="ui error message">div>
form>
<div class="ui message">
Already signed up? <a href="/login">Logina>
div>
div>
div>
body>
<script>
<!-- 注册账号-->
$("#registry").on("click", function () {
$.ajax({
url: "/user/create",
type: "POST",
data: {
email: $("#email").val(),
password: $("#password").val(),
},
resultType: "JSON",
success: function (result) {
alert(result.message)
},
error: function (result) {
result
}
})
});
script>
html>