[Spring实战] 整合Spring/SpringMVC/Mybatis(SSM)实现登录与注册功能(带前端)
目录
前言
功能展示
功能说明
数据库表设计
依赖
配置文件
后端
Controller
Entity实体类
Mapper
Service
Impl
前端
总结与改进方向
前言
不出意料,果然一个登录还是不够的 ,先说明一下,看这篇前请先去看一下上一篇这里放上上一篇的链接 [Spring实战] 整合Spring/SpringMVC/Mybatis(SSM)实现登录功能(带前端)_天海奈奈的博客-CSDN博客,对于项目的创建和依赖的配置这篇不会再讲,这篇主要在之前的基础上对数据库进行一定的修改,新写一个主页页面,实现注册功能。
功能展示
功能说明
之前只是在登录时验证密码时利用了MD5算法进行密码的转换与数据库中的密码去对比,注册时使用着这个工具类去将加密后的密码存到数据库中去,并在注册时检索数据库中是否已经有存在的用户名,在注册页面新加验证码功能,成功注册后自动跳转到登录页面。
数据库表设计
由于没有涉及到用户权限的功能所以没有使用到权值。这里会把sql放在这里直接复制运行就能得到这张表,但是记得去第一篇文章里面去改自己的数据库连接。
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for manager
-- ----------------------------
DROP TABLE IF EXISTS `manager`;
CREATE TABLE `manager` (
`manager_id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '管理员编号',
`username` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户名',
`password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码',
`salt` int(0) NOT NULL COMMENT '盐值',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '昵称',
PRIMARY KEY (`manager_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 520 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of manager
-- ----------------------------
INSERT INTO `manager` VALUES (1, 'MANAGER', 'ce20d2be56ca49d36ed2709e776187db', 1276, NULL, NULL);
INSERT INTO `manager` VALUES (516, 'user1', 'b8ddf129011519e083c25c6b05208da0', 1197, '2022-09-12 21:39:37', '用户1');
INSERT INTO `manager` VALUES (517, 'user2', '675f66d99f78db051bf8c73c3eff26a0', 1226, '2022-09-12 21:40:50', '拥护2');
INSERT INTO `manager` VALUES (518, 'user3', 'e8ee397e19a484c66c0edcde9c8e6c39', 1801, '2022-09-16 15:47:58', '用户三');
INSERT INTO `manager` VALUES (519, 'user4', '4dfcb81c7e01b6beaf0092ddbd8534fd', 1478, '2022-09-16 15:49:36', '用户4');
SET FOREIGN_KEY_CHECKS = 1;
依赖
<!-- 验证码-->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
配置文件
<!-- 验证码-->
<!-- 配置Kaptcha 完成对Kaptcha 初始化赋值的工作:字体大小,文本,背景色。。。。。。-->
<bean id="kaptchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">
<!-- 用于生成验证码图片-->
<property name="config">
<bean class="com.google.code.kaptcha.util.Config">
<constructor-arg>
<!-- 构造参数-->
<props>
<!--验证码图片不生成边框-->
<prop key="kaptcha.border">no</prop>
<!-- 验证码图片宽度为120像素 -->
<prop key="kaptcha.image.width">120</prop>
<!-- 验证码图片字体颜色为蓝色 -->
<prop key="kaptcha.textproducer.font.color">blue</prop>
<!-- 每个字符最大占用40像素 -->
<prop key="kaptcha.textproducer.font.size">40</prop>
<!-- 验证码包含4个字符 -->
<prop key="kaptcha.textproducer.char.length">4</prop>
</props>
</constructor-arg>
</bean>
依赖和配置文件在原基础上加就好
后端
Controller
@RestController
@RequestMapping("/api/manager")
public class ManagerController {
@Resource
private ManagerService managerService;
@PostMapping("/check_login1")
public ResponseUtils checkLogin1(String username , String password){
ResponseUtils resp ;
resp = new ResponseUtils();
try {
Manager manager = managerService.checkLogin1(username, password);
manager.setPassword(null);
manager.setSalt(null);
resp = new ResponseUtils().put("manager" , manager);
}catch (Exception e){
e.printStackTrace();
resp = new ResponseUtils(e.getClass().getSimpleName(), e.getMessage());
}
return resp;
}
@PostMapping("/register")
public ResponseUtils register(String username, String password, String nickname, String vc, HttpServletRequest request) {
String verifyCode = (String) request.getSession().getAttribute("kaptchaVerifyCode");
ResponseUtils resp;
if (vc == null || verifyCode == null || !vc.equalsIgnoreCase(verifyCode)) {
resp = new ResponseUtils("VerifyCodeError", "验证码错误");
} else {
resp = new ResponseUtils();
//验证码比对成功后进行用户注册
try {
Manager manager = managerService.createManager(username, password, nickname);
resp = new ResponseUtils();
} catch (Exception e) {
e.printStackTrace();
resp = new ResponseUtils(e.getClass().getSimpleName(), e.getMessage());
}
}
return resp;
}
}
与上一篇其实差不多,只是增加了验证码校验,调用新写了一个Service来创建对象。
Entity实体类
@TableName("manager")
public class Manager {
@TableId(type = IdType.AUTO)
private Long managerId;
private String username;
private String password;
private Integer salt;
private String nickname;
private Date createTime;
@Override
public String toString() {
return "Manager{" +
"managerId=" + managerId +
", username='" + username + '\'' +
", password='" + password + '\'' +
", salt=" + salt +
", nickname='" + nickname + '\'' +
", createTime=" + createTime +
'}';
}
public Long getManagerId() {
return managerId;
}
public void setManagerId(Long managerId) {
this.managerId = managerId;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Long getUserId() {
return managerId;
}
public void setUserId(Long userId) {
this.managerId = userId;
}
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 Integer getSalt() {
return salt;
}
public void setSalt(Integer salt) {
this.salt = salt;
}
}
新加创建时间和昵称
Mapper
public interface ManagerMapper extends BaseMapper<Manager> {
}
因为整合了mubatis-plus所以并不需要自己写mapper以及xml文件
Service
public interface ManagerService {
public Manager checkLogin1(String username, String password);
public Manager createManager(String username, String password, String nickname);
}
这里也就加一个新建对象
Impl
@Service
//默认方法不开启事务
@Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true)
public class ManagerServiceImpl implements ManagerService {
@Resource
private ManagerMapper managerMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public Manager checkLogin1(String username, String password) {
QueryWrapper<Manager> wrapper = new QueryWrapper();
wrapper.eq("username", username);
Manager manager = managerMapper.selectOne(wrapper);
if (manager == null) {
throw new MemberException("用户不存在");
}
String md5 = MD5Utils.md5Digest(password, manager.getSalt());
if (!md5.equals(manager.getPassword())) {
throw new MemberException("您输入的密码有误");
}
return manager;
}
@Override
public Manager createManager(String username, String password, String nickname) {
QueryWrapper<Manager> wrapper = new QueryWrapper<>();
wrapper.eq("username", username);
List<Manager> managers = managerMapper.selectList(wrapper);
if(managers.size() > 0 ){
throw new MemberException("用户已存在");
}
Manager manager = new Manager();
manager.setUsername(username);
manager.setNickname(nickname);
manager.setCreateTime(new Date());
int salt = new Random().nextInt(1000) + 1000;
manager.setSalt(salt);
String md5 = MD5Utils.md5Digest(password, salt);
manager.setPassword(md5);
managerMapper.insert(manager);
return manager;
}
}
实现类里面也就先去搜索数据库看里面有没有已经存在的用户名,如果没有我们就创建一个新的。
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
<!-- 引入样式 -->
<link rel="stylesheet" type="text/css" href="assets/element-plus/index.css">
<!-- 引入组件库 -->
<script src="/assets/vue/vue.global.js"></script>
<script src="/assets/element-plus/index.full.js"></script>
<script src="/assets/axios/axios.js"></script>
<style>
.login-box {
border: 1px solid #DCDFE6;
width: 350px;
margin: 180px auto;
padding: 35px 35px 15px 35px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow: 0 0 25px #909399;
}
.login-title{
text-align: center;
margin: 0 auto 40px auto;
color: #303133;
}
</style>
</head>
<body>
<div id="app">
<el-form ref="registerForm" label-width="80px" :rules="rules" :model="form" class="login-box">
<h2 class="login-title">注册</h2>
<el-form-item label="账号" prop="username">
<el-input type="text" placeholder="请输入账号" v-model="form.username"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" placeholder="请输入密码" v-model="form.password"></el-input>
</el-form-item>
<el-form-item label="昵称" prop="nickname">
<el-input type="text" placeholder="请输入昵称" v-model="form.nickname"></el-input>
</el-form-item>
<el-form-item label="验证码" prop="verifyCode">
<el-row>
<el-col :span="16">
<el-input type="verifyCode" placeholder="请输入验证码" v-model="form.verifyCode"></el-input>
</el-col>
<el-col :span="8">
<img id="imgVerifyCode" src="/api/verify_code"
style="width: 100%;height:44px;cursor: pointer" @click="reloadVerifyCode">
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="onSubmit('registerForm')" style="width:200px">注册</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="returnBack" style="width:200px">退出</el-button>
</el-form-item>
</el-form>
</div>
<script>
const Main = {
data() {
return {
form: {
username: ''
,password: ''
,verifyCode:''
,nickname:''
}
,rules:{
username: [
{required: true,message : '账号不能为空' , trigger:'blur'}
],
password:[
{required: true,message : '密码不能为空' , trigger:'blur'}
],
verifyCode: [
{required:true,message : '验证码不能为空' , trigger:'blur'}
],
nickname:[
{required:true,message : '昵称不能为空' , trigger:'blur'}
]
}
}
}
,methods : {
onSubmit(formName){
const objApp = this;
const form = this.$refs[formName];
form.validate((valid) => {
if(valid){
console.info("表单校验成功,准备提交数据");
const form = this.form;
const $message = this.$message;
const params = new URLSearchParams();
params.append("username", form.username);
params.append("password", form.password);
params.append("nickname", form.nickname);
params.append("vc", form.verifyCode);
axios.post("/api/manager/register", params)
.then(function (response) {
console.info(response);
const json = response.data;
if(json.code=="0"){
$message.success({message:"注册成功,进入登录界面", offset: 100});
window.location.href = "/login.html";
}else{
$message.error({message:json.message, offset: 100});
}
});
}
})
}
,returnBack : function (){
window.location.href = "/login.html";
}
, reloadVerifyCode: function () { //刷新验证码方案
document.getElementById("imgVerifyCode").src = "/api/verify_code?ts=" + new Date().getTime();
}
}
};
//初始化Vue,绑定Main中的数据,利用ElementPlus对#app容器进行重新渲染
const app = Vue.createApp(Main);
app.use(ElementPlus);
app.mount("#app");
</script>
</body>
</html>
总结与改进方向
下次会更新验证码将网页实时生成的验证码改为邮箱验证,到此为止还是没有使用到session,如果有需求可以自己加,到这里有着基础功能的注册与登录功能的部分就实现了。这个前端登录页面也可以作为一个通用化的页面,只需要改变里面的的api接口就能做到重复使用。