springboot+vue+MySQL登录系统
项目结构
MySQL数据库
-- CREATE DATABASE xm_exam;
use `xm_exam`;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS sys_user;
# 用户
create table sys_user(
user_id int PRIMARY KEY auto_increment not null,
user_name varchar(20) not null,#用户名。
user_password varchar(20) not null,#密码。
real_name varchar(20) not null,#真实姓名
id_card varchar(18) not null,#身份证
phone varchar(20) not null,#联系方式
role int not null default 2,#0管理,1老师,2考生
state int not null default 0, #用户状态0正常1隐藏
is_proctor int default 0 #监考0不是1是
)CHARSET=utf8;
insert into sys_user values
(1,'adm','1234','管理','123123123412121234','111',0,0,0),
(2,'tea','1234','老师','123123123412121234','112',1,0,1),
(3,'stu','1234','学生','123123123412121234','113',2,0,0),
(4,'12','12','12','12','123123123412121234',2,0,0);
SET FOREIGN_KEY_CHECKS = 1;
前端
vue.config.js
module.exports = {
lintOnSave: false, // 关闭语法检测
// 开启代理服务器
devServer: {
// 代理服务器可以将路由中的指定前缀转发到指定的后端服务器中
proxy: {
'/api': {
target: 'http://localhost:8081',
ws: true, // 是否启用websockets
changeOrigin: true, // 代理时是否更改host
pathRewrite: {
'^/api': '' //这里理解成用'/api'代替target里面的地址,比如我要调用'http://40.00.100.100:3002/user/add',直接写'/api/user/add'即可
}
}
}
}
}
package.json
{
"name": "vue-login-demo",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.25.0",
"core-js": "^3.6.5",
"element-ui": "^2.15.6",
"vue": "^2.6.11",
"vue-axios": "^3.4.0",
"vue-router": "^3.5.3"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"vue-template-compiler": "^2.6.11"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
main.js
import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import VueRouter from 'vue-router'
import router from './router'
import axios from 'axios'
import VueAxios from 'vue-axios'
// 关闭 Vue 的生产提示
Vue.config.productionTip = false
// 使用插件
Vue.use(ElementUI) // element ui 插件
Vue.use(VueRouter) // 路由插件
Vue.use(VueAxios, axios) // 使用 axios 插件
// 创建 Vue 实例对象
new Vue({
render: h => h(App), // render 函数将帮助解析模板,传入的参数 h 为一个函数,该函数可用来解析 App 这个组件
router
}).$mount('#app') // 将 App.vue 组件挂载到 index.html 中的 id 为 app 的 div 标签上
APP.vue
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: 'App',
components: {
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Home.vue
<template>
<div>
<h2>欢迎{{ user.userName }}!您的 uid 为{{ user.userId }}</h2>
<el-button @click="logout"> 登出 </el-button>
</div>
</template>
<script>
export default {
data() {
return {
user: {
userName: "",
userId: null,
},
};
},
methods: {
logout(){
// 移除本地用户登录信息
sessionStorage.removeItem('userInfo');
// 跳转页面到登录页
this.$router.push('/login');
}
},
mounted() {
if (sessionStorage.getItem('userInfo')) {
// 将用户信息存储到sessionStorage中
this.user = JSON.parse(sessionStorage.getItem('userInfo'));
}
},
};
</script>
<style scoped>
</style>
Login.vue
<template>
<div>
<el-card class="box-card">
<h2>登录</h2>
<el-form
:model="ruleForm"
status-icon
:rules="rules"
ref="ruleForm"
label-position="left"
label-width="70px"
class="login-from"
>
<el-form-item label="用户名" prop="userName">
<el-input v-model="ruleForm.userName"></el-input>
</el-form-item>
<el-form-item label="密码" prop="userPassword">
<el-input
type="password"
v-model="ruleForm.userPassword"
autocomplete="off"
></el-input>
</el-form-item>
</el-form>
<div class="btnGroup">
<el-button
type="primary"
@click="submitForm('ruleForm')"
v-loading="loading"
>登录</el-button
>
<el-button @click="resetForm('ruleForm')">重置</el-button>
<router-link to="/register">
<el-button style="margin-left: 10px">注册</el-button>
</router-link>
</div>
</el-card>
</div>
</template>
<script>
export default {
data() {
return {
ruleForm: {
userName: "",
userPassword: "",
},
rules: {
userName: [
{ required: true, message: "用户名不能为空!", trigger: "blur" },
],
userPassword: [
{ required: true, message: "密码不能为空!", trigger: "blur" },
],
},
loading: false, // 是否显示加载动画
};
},
methods: {
submitForm(formName) {
// 验证表单中的账号密码是否有效,因为在上面rules中定义为了必填 required: true
this.$refs[formName].validate((valid) => {
// 点击登录后,让登录按钮开始转圈圈(展示加载动画)
this.loading = true;
// 如果经过校验,账号密码都不为空,则发送请求到后端登录接口
if (valid) {
let _this = this;
// 使用 axios 将登录信息发送到后端
this.axios({
url: "/api/sys_user/login", // 请求地址
method: "post", // 请求方法
headers: { // 请求头
"Content-Type": "application/json",
},
params: { // 请求参数
userName: _this.ruleForm.userName,
userPassword: _this.ruleForm.userPassword,
},
}).then((res) => { // 当收到后端的响应时执行该括号内的代码,res 为响应信息,也就是后端返回的信息
if (res.data.code === "0") { // 当响应的编码为 0 时,说明登录成功
// 将用户信息存储到sessionStorage中
sessionStorage.setItem("userInfo", JSON.stringify(res.data.data));
// 跳转页面到首页
this.$router.push('/home');
// 显示后端响应的成功信息
this.$message({
message: res.data.msg,
type: "success",
});
} else { // 当响应的编码不为 0 时,说明登录失败
// 显示后端响应的失败信息
this.$message({
message: res.data.msg,
type: "warning",
});
}
// 不管响应成功还是失败,收到后端响应的消息后就不再让登录按钮显示加载动画了
_this.loading = false;
console.log(res);
});
} else { // 如果账号或密码有一个没填,就直接提示必填,不向后端请求
console.log("error submit!!");
this.loading = false;
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
},
};
</script>
<style scoped>
/* 设置登录面板居中,宽度为400px */
.box-card {
margin: auto auto;
width: 400px;
}
/* 设置登录面板中的表单居中 */
.login-from {
margin: auto auto;
}
</style>
Register.vue
<template>
<div>
<el-card class="box-card">
<h2>注册</h2>
<el-form
:model="ruleForm"
status-icon
:rules="rules"
ref="ruleForm"
label-position="left"
label-width="80px"
class="demo-ruleForm"
>
<el-form-item label="用户名" prop="userName">
<el-input v-model="ruleForm.userName"></el-input>
</el-form-item>
<el-form-item label="密码" prop="pass">
<el-input
type="password"
v-model="ruleForm.pass"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="userPassword">
<el-input
type="password"
v-model="ruleForm.userPassword"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item label="xm" prop="realName">
<el-input v-model="ruleForm.realName"></el-input>
</el-form-item>
<el-form-item label="身份证" prop="idCard">
<el-input v-model="ruleForm.idCard"></el-input>
</el-form-item>
<el-form-item label="联系" prop="phone">
<el-input v-model="ruleForm.phone"></el-input>
</el-form-item>
</el-form>
<div class="btnGroup">
<el-button type="primary" @click="submitForm('ruleForm')" v-loading="loading"
>提交</el-button
>
<el-button @click="resetForm('ruleForm')">重置</el-button>
<el-button @click="goBack">返回</el-button>
</div>
</el-card>
</div>
</template>
<script>
export default {
data() {
var validatePass = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入密码"));
} else {
if (this.ruleForm.checkPass !== "") {
this.$refs.ruleForm.validateField("checkPass");
}
callback();
}
};
var validatePass2 = (rule, value, callback) => {
if (value === "") {
callback(new Error("请再次输入密码"));
} else if (value !== this.ruleForm.pass) {
callback(new Error("两次输入密码不一致!"));
} else {
callback();
}
};
return {
ruleForm: {
userName: "",
pass: "",
userPassword: "",
realName: "",
idCard: "",
phone: "",
},
rules: {
userName: [{ required: true, message: "用户名不能为空!", trigger: "blur" },],
pass: [{ required: true, validator: validatePass, trigger: "blur" }],
userPassword: [{ required: true, validator: validatePass2, trigger: "blur" },],
realName: [{ required: true, message: "不能为空!", trigger: "blur" },],
idCard: [{ required: true, message: "不能为空!", trigger: "blur" },],
phone: [{ required: true, message: "不能为空!", trigger: "blur" },],
},
loading: false
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
this.loading = true; // 提交按钮显示加载动画
if (valid) {
let _this = this;
this.axios({ // axios 向后端发起请求
url: "/api/sys_user/register", // 请求地址
method: "post", // 请求方法
headers: { // 请求头
"Content-Type": "application/json",
},
data: { // 请求参数,为 data,与登录的 params 不太一样
userName: _this.ruleForm.userName,
userPassword: _this.ruleForm.userPassword,
realName: _this.ruleForm.realName,
idCard: _this.ruleForm.idCard,
phone: _this.ruleForm.phone,
role: 2,
state: 0,
is_proctor: 0,
},
}).then((res) => { // 当收到后端的响应时执行该括号内的代码,res 为响应信息,也就是后端返回的信息
if (res.data.code === '0') { // 当响应的编码为 0 时,说明注册成功
// 显示后端响应的成功信息
this.$message({
message: res.data.msg,
type: "success",
});
}else{ // 当响应的编码不为 0 时,说明注册失败
// 显示后端响应的失败信息
this.$message({
message: res.data.msg,
type: "warning",
});
}
// 不管响应成功还是失败,收到后端响应的消息后就不再让登录按钮显示加载动画了
_this.loading = false;
console.log(res);
});
} else { // 如果账号或密码有一个没填,就直接提示必填,不向后端请求
console.log("error submit!!");
this.loading = false;
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
goBack() {
this.$router.go(-1);
},
},
};
</script>
<style scoped>
/* 设置登录面板居中,宽度为400px */
.box-card {
margin: auto auto;
width: 400px;
}
/* 设置登录面板中的表单居中 */
.login-from {
margin: auto auto;
}
</style>
index.js
// 此文件专门负责项目的路由
import VueRouter from "vue-router"
// 引入组件
import Login from '../views/login/Login'
import Register from '../views/register/Register'
import Home from '../views/home/Home'
import { Message } from "element-ui";
// 创建并暴露一个路由器
const router = new VueRouter({
mode: 'history', // 路由模式,该模式不会在地址中显示井号#
routes: [
{
path: '/', // 路径
redirect: '/login' // 重定向
},
{
path: '/login', // 路径
component: Login // 跳转到的组件
},
{
path: '/register', // 路径
component: Register // 跳转到的组件
},
{
path: '/home', // 路径
component: Home // 跳转到的组件
}
]
})
// 导航守卫,前置处理
router.beforeEach((to, from, next) => {
let isAuthenticated = !!sessionStorage.getItem('userInfo')
// 如果路由要跳转到除了登录和注册的界面的话就判断是否已经登录,如果没有登录就强制跳到登录界面
if (to.path !== '/login' && to.path !== '/register' && !isAuthenticated) {
next({ path: '/login' })
Message({
message: '请先登录!',
type: "warning",
});
} else next()
})
export default router;
后端
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.3.12.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.springboot</groupId>
<artifactId>springboot-login-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-login-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
server.port=8081
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/xm_exam?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=1234
GlobalCorsConfig.java
package com.springboot.springbootlogindemo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class GlobalCorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") //添加映射路径,“/**”表示对所有的路径实行全局跨域访问权限的设置
.allowedOrigins("*") //开放哪些ip、端口、域名的访问权限
.allowCredentials(true) //是否允许发送Cookie信息
.allowedMethods("GET", "POST", "PUT", "DELETE") //开放哪些Http方法,允许跨域访问
.allowedHeaders("*") //允许HTTP请求中的携带哪些Header信息
.exposedHeaders("*"); //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
}
};
}
}
package com.springboot.springbootlogindemo.controller;
import com.springboot.springbootlogindemo.domain.SysUser;
import com.springboot.springbootlogindemo.service.SysUserService;
import com.springboot.springbootlogindemo.utils.Result;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
@RequestMapping("/sys_user")
public class SysUserController {
@Resource
private SysUserService sysUserService;
@PostMapping("/login")
public Result<SysUser> loginController(@RequestParam String userName, @RequestParam String userPassword){
SysUser user = sysUserService.loginService(userName, userPassword);
if(user!=null){
return Result.success(user,"登录成功!");
}else{
return Result.error("123","账号或密码错误!");
}
}
@PostMapping("/register")
public Result<SysUser> registController(@RequestBody SysUser newUser){
SysUser user = sysUserService.registService(newUser);
if(user!=null){
return Result.success(user,"注册成功!");
}else{
return Result.error("456","用户名已存在!");
}
}
}
SysUser.java
package com.springboot.springbootlogindemo.domain;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author hw
*/
@Table(name = "sys_user")
@Entity
public class SysUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer userId;
private String userName;
private String userPassword;
private String realName;
private String idCard;
private String phone;
private Integer role;
private Integer state;
private Integer isProctor;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String getRealName() {
return realName;
}
public void setRealName(String realName) {
this.realName = realName;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Integer getRole() {
return role;
}
public void setRole(Integer role) {
this.role = role;
}
public Integer getState() {
return state;
}
public void setState(Integer state) {
this.state = state;
}
public Integer getIsProctor() {
return isProctor;
}
public void setIsProctor(Integer isProctor) {
this.isProctor = isProctor;
}
}
SysUserDao.java
package com.springboot.springbootlogindemo.repository;
import com.springboot.springbootlogindemo.domain.SysUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SysUserDao extends JpaRepository<SysUser, Long> {
SysUser findByUserName(String userName); //通过用户名uname查找用户,注意要按照JPA的格式使用驼峰命名法
SysUser findByUserNameAndUserPassword(String userName, String userPassword);//通过用户名uname和密码查找用户
}
SysUserService.java
package com.springboot.springbootlogindemo.service;
import com.springboot.springbootlogindemo.domain.SysUser;
public interface SysUserService {
/**
* 登录业务逻辑
* @param userName 账户名
* @param userPassword 密码
* @return
*/
SysUser loginService(String userName, String userPassword);
/**
* 注册业务逻辑
* @param user
* @return
*/
SysUser registService(SysUser user);
}
SysUserServiceImpl.java
package com.springboot.springbootlogindemo.service.serviceImpl;
import com.springboot.springbootlogindemo.domain.SysUser;
import com.springboot.springbootlogindemo.repository.SysUserDao;
import com.springboot.springbootlogindemo.service.SysUserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class SysUserServiceImpl implements SysUserService {
@Resource
private SysUserDao sysUserDao;
@Override
public SysUser loginService(String userName, String userPassword) {
// 如果账号密码都对则返回登录的用户对象,若有一个错误则返回null
SysUser user = sysUserDao.findByUserNameAndUserPassword(userName, userPassword);
// 重要信息置空
if(user != null){
user.setUserPassword("");
}
return user;
}
@Override
public SysUser registService(SysUser user) {
//当新用户的用户名已存在时
if(sysUserDao.findByUserName(user.getUserName())!=null){
// 无法注册
return null;
}else{
//返回创建好的用户对象(带uid)
SysUser newUser = sysUserDao.save(user);
if(newUser != null){
newUser.setUserPassword("");
newUser.setRealName("");
newUser.setIdCard("");
newUser.setPhone("");
}
return newUser;
}
}
}
Result.java
package com.springboot.springbootlogindemo.utils;
public class Result<T> {
private String code;
private String msg;
private T data;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Result() {
}
public Result(T data) {
this.data = data;
}
public static Result success() {
Result result = new Result<>();
result.setCode("0");
result.setMsg("成功");
return result;
}
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>(data);
result.setCode("0");
result.setMsg("成功");
return result;
}
public static <T> Result<T> success(T data,String msg) {
Result<T> result = new Result<>(data);
result.setCode("0");
result.setMsg(msg);
return result;
}
public static Result error(String code, String msg) {
Result result = new Result();
result.setCode(code);
result.setMsg(msg);
return result;
}
}