目录
创建springboot项目
目录结构:
启动类
测试类
idea建表
pom文件
编写yml文件
qq邮箱设置
登陆注册代码
编写持久层(Dao)
注册代码
业务层
业务实现类
mapper
控制层
前端代码
注册页面
邮件正文:
登录代码
控制层
业务层:
业务实现类:
前端代码
自动登录
创建springboot项目
Maven类型+
Lombok依赖+spring web依赖+MySQL driver依赖+mybatis framework依赖+java Mail Sender依赖
目录结构:
有些类是自己写的不用看,看一下结构就可以
启动类
@SpringBootApplication @MapperScan("com.example.mapper")//将mapper路径下自动注册映射器接口为spring Bean public class SpringBootDemo1Application { public static void main(String[] args) { SpringApplication.run(SpringBootDemo1Application.class,args); } }
测试类
@SpringBootTest @RunWith(SpringRunner.class) class SpringBootTestApplicationTests { }
idea建表
use `数据库名`;
create table `user`(
`id` int primary key auto_increment comment '主键id',
`email` varchar(255) comment '用户邮箱',
`password` varchar(255) comment '用户密码',
`salt` varchar(255) comment '盐',
`confirm_code` varchar(255) comment '确认码',
`activation_time` datetime comment '激活时间',
`is_valid` tinyint comment '账号状态'
)default charset=utf8 comment '用户表';
pom文件
<?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>
<groupId>com.example</groupId>
<artifactId>springBoot-Test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springBoot-Test</name>
<description>springBoot-Test</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.6.13</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--导入Mybatis坐标-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<!--导入mybatis-plus坐标-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<!--导入数据库druid坐标-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.22</version>
</dependency>
<!--简略get,set方法-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<!--模版引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!--添加事务管理-->
<!-- 支持使用 Spring AOP 和 AspectJ 进行切面编程。 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- SpringBoot集成FreeMarker的依赖 :模版引擎,生成输出文本-->
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</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-mail</artifactId>
</dependency>
<!--工具包:雪花算法:生成确认码-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.25</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.example.springboottest.SpringBootTestApplication</mainClass>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
编写yml文件
server: port: 8088 spring: datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver # 配置数据库驱动程序,用于数据库通信和执行sql语句 url: jdbc:mysql://localhost:3306/表名?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8 # 加数据库名 username: root password: mvc: # 处理springboot与swagger2冲突 pathmatch: matching-strategy: ant_path_matcher freemarker: expose-session-attributes: true # 暴露session对象的属性 settings: classic_compatible: true # 配置为传统模式,控制自动处理 suffix: .ftl # 指定模版文件后缀 mail: protocol: smtp # 邮箱协议 host: smtp.qq.com # qq邮箱,如果用网易将qq改成163 port: 25 username: # 发送人邮件名(这个地方可能出错,可能是由于名字问题) password: # 授权码 default-encoding: utf-8 properties: mail: debug: true # 开启debug模式会打印邮件发送过程的日志 mybatis-plus: # mybatis映射器XML文件位置 作用:定义sql语句和结果映射 mapper-location: classpath:mapper/*.xml # 开启mybatis-plus的运行日志(建议) configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
qq邮箱设置
点击第二列的账号:开启服务,发送邮件,记下授权码 ok !
登陆注册代码
编写持久层(Dao)
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "user")
public class User {
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
@TableField(value = "email")
private String email;
@TableField(value = "password") // 密码,使用md5+盐加密
private String password;
@TableField(value = "salt")
private String salt; // 盐
@TableField(value = "confirm_code") // 确认码
private String confirmCode;
@TableField(value = "activation_time") // 激活失效时间
private LocalDateTime activationTime;
@TableField(value = "is_valid") // 账号状态
private Byte isValid;
}
注册代码
业务层
public interface UserService extends IService<User> {
// 创建账号
Map<String,Object> createAccount(User user);
// 创建账号过程中,用于激活账号的确认码,没有激活不可以登录账号
Map<String, Object> activation(String confirmCode);
// 将用户信息录入数据库
int insertUser(User user);
// 通过确认码查找用户,不用担心确认码重复
User selectUserByConfirmCode(String confirmCode);
// 通过邮箱激活账号
int updateUserByConfirmCode(String confirmCode);
// 通过邮箱名查找用户(如果没有进行注册查重,那么将这个修改成List<User>有多个用户报异常)
User selectUserByEmail(String email);
}
业务实现类
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper um;
@Value("${spring.mail.username}")
private String mailUsername;
@Resource //发送邮件,实现邮箱验证
private JavaMailSender javaMailSender;
@Resource // 模版引擎
private TemplateEngine templateEngine;
@Override
@Transactional // 事务管理
/*
* 创建账号:接收发送过来的邮件账号和密码,利用雪花算法生成确认码用于激活账号,使用MD5将密码进行加密
* 设置激活时间和激活状态为未激活后添加到数据库,发送邮件领取激活码,激活账号*/
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);
// 激活时间 有效时间
LocalDateTime ldt = LocalDateTime.now().plusDays(1);
user.setConfirmCode(confirmCode);
user.setSalt(salt);
user.setPassword(md5Pwd);
user.setActivationTime(ldt);
user.setIsValid((byte)0);
// 新增账号
int result = um.insert(user);
HashMap<String, Object> hm = new HashMap<>();
if(result > 0){
// 发送邮件:前端会调用该路径下的方法并传递确认码参数
String activationUrl = "http://localhost:8088/user/activation?confirmCode=" + confirmCode;
sendMailForActivationAccount(activationUrl,user.getEmail());
hm.put("code",200);
hm.put("message","注册成功。请前往邮件进行账号激活");
}else {
hm.put("code",400);
hm.put("message","注册失败");
}
return hm;
}
@Override
@Transactional
/*发送邮件申请激活账号:创建邮件,设置主题、发送者、接受者、发送日期、创建上下文环境(编写在
html)*/
public void sendMailForActivationAccount(String activationUrl, String email) {
// 创建邮件对象
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
try {
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true);
// 设置邮箱主题
message.setSubject("抖音账号激活");
// 设置邮件发送者
message.setFrom(mailUsername);
// 设置邮件接受者(可以设置多个)
message.setTo(email);
// 设置邮件发送日期
message.setSentDate(new Date());
// 创建上下文环境(正文) 用thymeleaf
Context context = new Context();
// 将url注入html中,在html中直接用
// 当在页面点击链接时,会直接执行路径中方法
context.setVariable("activationUrl",activationUrl);
String text = templateEngine.process("activation-account.html",context);
message.setText(text,true);
} catch (MessagingException e) {
throw new RuntimeException(e);
}
// 发送邮件
javaMailSender.send(mimeMessage);
}
@Override
/*在注册阶段会调用该方法实现账号激活:根据注册阶段传递过来的确认码查询用户,调取用户信息
判断是否是有效激活时间,返回结果*/
public Map<String, Object> activation(String confirmCode) {
Map<String, Object> hm = new HashMap<>();
// 根据确认码查询用户
User user = um.selectUserByConfirmCode(confirmCode);
// 判断激活时间是否失效:现在时间是在激活时间之后吗
boolean after = LocalDateTime.now().isAfter(user.getActivationTime());
if(after){
hm.put("code",400);
hm.put("message","激活失效,请重启注册");
return hm;
}
// 修改状态值
int result = um.updateUserByConfirmCode(confirmCode);
if(result > 0){
hm.put("code",200);
hm.put("message","激活成功");
return hm;
}else {
hm.put("code",400);
hm.put("message","激活失效,请联系管理员");
return hm;
}
}
@Override
public int insertUser(User user) {
return um.insert(user);
}
@Override
public User selectUserByConfirmCode(String confirmCode) {
return um.selectUserByConfirmCode(confirmCode);
}
@Override
public int updateUserByConfirmCode(String confirmCode) {
return um.updateUserByConfirmCode(confirmCode);
}
@Override
public User selectUserByEmail(String email) {
return um.selectUserByEmail(email);
}
}
mapper
public interface UserMapper extends BaseMapper<User> {
/*
* 根据确认码查询用户*/
@Select("SELECT email,activation_time FROM user WHERE confirm_code = #{confirmCode} AND is_valid = 0")
User selectUserByConfirmCode(@Param("confirmCode") String confirmCode);
/*
* 根据确认码查询用户并修改状态码为1*/
@Update("UPDATE user SET is_valid = 1 WHERE confirm_code = #{confirmCode}")
int updateUserByConfirmCode(@Param("confirmCode") String confirmCode);
/*
* 根据邮箱查询用户*/
@Select("SELECT * FROM user WHERE email = #{email} AND is_valid = 1")
User selectUserByEmail(@Param("email") String email);
}
控制层
@Api("用户界面")
@RestController
@RequestMapping("user")
public class UserController {
@Resource
private UserService us;
@ApiOperation(value = "注册账号")
@PostMapping("create")
public Map<String,Object> createAccount(@RequestBody User user){
return us.createAccount(user);
}
@ApiOperation(value = "激活账号")
@GetMapping("activation")
/*通过邮件激活相当于网页传递数据。因为路径中追加了confirmCode所以会自动传参*/
public Map<String,Object> activation(String confirmCode){
return us.activation(confirmCode);
}
}
前端代码
因为用的是thymeleaf写的所以目录结构如下(记得写在templates中)
注册页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引入 jQuery -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
body {
background: url('https://cdn.pixabay.com/photo/2018/08/14/13/23/ocean-3605547_1280.jpg') no-repeat;
background-size: 100% 130%;
}
#login_box {
width: 20%;
height: 400px;
background-color: #00000060;
margin: auto;
margin-top: 10%;
text-align: center;
border-radius: 10px;
padding: 50px 50px;
}
h2 {
color: #ffffff90;
margin-top: 5%;
}
#input-box {
margin-top: 5%;
}
span {
color: #fff;
}
input {
border: 0;
width: 60%;
font-size: 15px;
color: #fff;
background: transparent;
border-bottom: 2px solid #fff;
padding: 5px 10px;
outline: none;
margin-top: 10px;
}
button {
margin-top: 50px;
width: 60%;
height: 30px;
border-radius: 10px;
border: 0;
color: #fff;
text-align: center;
line-height: 30px;
font-size: 15px;
background-image: linear-gradient(to right, #30cfd0, #330867);
}
#sign_up {
margin-top: 45%;
margin-left: 60%;
}
a {
color: #b94648;
}
</style>
</head>
<body>
<div id="login_box">
<h2>REGISTRY</h2>
<div id="input_box">
<input type="text" id = "email" name="email" placeholder="请输入用户名">
</div>
<div class="input_box">
<input type="password" id = "password" name="password" placeholder="请输入密码">
</div>
<button id = "registry">注册</button><br><br>
<p>Come to us ? <a href="login">Login</a></p>
</div>
/*当id:registry发生点击行为thymeleaf会接受相应过来的数据并转换成json格式,然后调用url路径下的方法创建账号
* result接收控制层返回的数据(Map)在网页提示创建成功或创建失败*/
<script type="text/javascript" charset="UTF-8">
$("#registry").on("click",function (){
$.ajax({
url:"/user/create",
type: "POST",
contentType: "application/json",
data: JSON.stringify({
email: $("#email").val(),
password: $("#password").val()
}),
dataType: "json",
success: function(result){
alert(result.message);
},
error: function(result){
alert("发生错误");
}
});
});
</script>
</body>
</html>
邮件正文:
<html>
<body>
<!--根据自己需要编写,-->
<div>
<p>Email 地址验证</p> <!--target:新的窗口打开超链接-->
<p>这封信是由<a href="https://www.douyin.com" target = "_blank">抖音</a>发送的</p>
<p>
由于你在抖音进行了新用户注册或用户修改,如果你没有进行过此操作,请忽略这封邮件。如果你是新用户或者修改你的账号
请点击下方链接:
</p>
<p>
<a th:href="@{${activationUrl}}"><span th:text="${activationUrl}"></span></a>
感谢你的访问,祝你生活愉快。
</p>
</div>
</body>
</html>
登录代码
控制层
登录界面:http://localhost:8088/gameLogin/login
@Api(value = "登录页面")
@Controller
@RequestMapping("gameLogin")
public class LoginController {
@Resource
private UserService us;
@GetMapping("login")
public String login(){
return "login";
}
@GetMapping("registry")
public String registry(){
return "registry";
}
@PostMapping("loginAccount")
@ResponseBody
public Map<String,Object> loginAccount(@RequestBody User user){
return us.loginAccount(user);
}
}
业务层:
public interface UserService extends IService<User> {
Map<String,Object> loginAccount(User user);
}
业务实现类:
@Override
/*
* 登录账号*/
public Map<String, Object> loginAccount(User user) {
Map<String, Object> hm = new HashMap<>();
User user1 = um.selectUserByEmail(user.getEmail());
// 如果账号不存在或未激活
if(user1 == null || user1.getIsValid() == (byte)0){
hm.put("code",400);
hm.put("message","该账户不存在或未激活");
return hm;
}
// 进行密码比对
String md5Pwd = SecureUtil.md5(user.getPassword()+user1.getSalt());
if(!md5Pwd.equals(user1.getPassword())){
hm.put("code",400);
hm.put("message","用户名或密码错误");
return hm;
}
hm.put("code",200);
hm.put("message","登陆成功");
return hm;
}
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引入 jQuery -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
body {
background: url('https://cdn.pixabay.com/photo/2018/08/14/13/23/ocean-3605547_1280.jpg') no-repeat;
background-size: 100% 130%;
}
#login_box {
width: 20%;
height: 400px;
background-color: #00000060;
margin: auto;
margin-top: 10%;
text-align: center;
border-radius: 10px;
padding: 50px 50px;
}
h2 {
color: #ffffff90;
margin-top: 5%;
}
#input-box {
margin-top: 5%;
}
span {
color: #fff;
}
input {
border: 0;
width: 60%;
font-size: 15px;
color: #fff;
background: transparent;
border-bottom: 2px solid #fff;
padding: 5px 10px;
outline: none;
margin-top: 10px;
}
button {
margin-top: 50px;
width: 60%;
height: 30px;
border-radius: 10px;
border: 0;
color: #fff;
text-align: center;
line-height: 30px;
font-size: 15px;
background-image: linear-gradient(to right, #30cfd0, #330867);
}
#sign_up {
margin-top: 45%;
margin-left: 60%;
}
a {
color: #b94648;
}
</style>
</head>
<body>
<div id="login_box">
<h2>LOGIN</h2>
<div id="input_box">
<input type="text" id="email" name="email" placeholder="请输入用户名">
</div>
<div class="input_box">
<input type="password" id="password" name="password" placeholder="请输入密码">
</div>
<button id = "login">登录</button><br><br>
<p>New to us ? <a href="registry">Registry</a></p>
</div>
<script type="text/javascript" charset="UTF-8">
$("#login").on("click",function (){
$.ajax({
url:"/gameLogin/loginAccount",
type: "POST",
contentType: "application/json",
data: JSON.stringify({ // 将对象转化为json格式
email: $("#email").val(),
password: $("#password").val()
}),
dataType: "json",
success: function(result){
alert(result.message);
if(200 == result.code) {
window.location.href = "https://www.douyin.com"
}
},
error: function(result){
alert("发生错误");
}
});
});
</script>
</body>
</html>
自动登录
package com.example.utils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringBootConfiguration;
@SpringBootConfiguration
public class AutoStartLogin implements CommandLineRunner {
@Override
public void run(String ... args) {
try {
Runtime.getRuntime().exec("cmd /c start http://localhost:8088/gameLogin/login");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}