springboot+vue登录系统

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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值