Vue+SpringBoot用户登录,验证码功能实现

avatar 2020年05月18日14:33:18 2 10026 views
博主分享免费Java教学视频,B站账号:Java刘哥 ,长期提供技术问题解决、项目定制:本站商品点此
周末帮一个朋友写了一个SpringBoot+Vue的项目,抽时间整理一下里面的常用的技术点。
前端使用的 Element UI 模板。
代码地址:https://github.com/saysky/petition-system-server

一、效果图和项目结构

1.先看下效果图

2、然后贴一下前端项目结构

Login.vue 是登录页面的 vue 文件,Identify.vue 是验证码的

二、vue 前端代码

Login.vue
<template>
<div class="login-wrap">
<div class="ms-title">XX市信访系统-欢迎登录</div>
<div class="ms-login">
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="0px" class="demo-ruleForm">
<div v-if="errorInfo" style="margin-bottom: 5px;">
<span>{{errInfo}}</span>
</div>
<el-form-item prop="name">
<el-input v-model="ruleForm.name" placeholder="账号"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input type="password" placeholder="密码" v-model="ruleForm.password"
@keyup.enter.native="submitForm('ruleForm')"></el-input>
</el-form-item>
<el-form-item prop="validate">
<el-input v-model="ruleForm.validate" class="validate-code" placeholder="验证码"></el-input>
<div class="code" @click="refreshCode">
<s-identify :identifyCode="identifyCode"></s-identify>
</div>
</el-form-item>
<div class="login-btn">
<el-button type="primary" @click="submitForm('ruleForm')">登录</el-button>
</div>
<p class="register" @click="gotoRegister()">注册</p>
</el-form>
</div>
</div>
</template>
<script>
export default {
name: 'login',
data() {
return {
identifyCodes: "1234567890",
identifyCode: "",
errorInfo: false,
ruleForm: {
name: '',
password: '',
validate: ''
},
rules: {
name: [
{required: true, message: '请输入登录名', trigger: 'blur'}
],
password: [
{required: true, message: '请输入密码', trigger: 'blur'}
],
validate: [
{required: true, message: '请输入验证码', trigger: 'blur'}
]
}
}
},
mounted() {
this.identifyCode = "";
this.makeCode(this.identifyCodes, 4);
},
methods: {
submitForm(formName) {
const self = this;
self.$refs[formName].validate((valid) => {
if (valid) {
if (self.ruleForm.validate != this.identifyCode) {
self.errorInfo = true;
self.errInfo = '验证码错误';
self.$message.error('验证码错误');
this.refreshCode();
} else {
self.$http.post('/api/user/login', self.ruleForm)
.then((response) => {
console.log(response);
if (response.data.code == -1) {
self.errorInfo = true;
self.errInfo = response.data.msg;
self.$message.error(response.data.msg);
// 重新生成验证码
this.refreshCode()
} else if (response.data.code == 0) {
self.$message.success(response.data.msg);
self.$router.push('/home');
sessionStorage.setItem('session_username', self.ruleForm.name);
sessionStorage.setItem('session_user', JSON.stringify(self.ruleForm));
console.log(JSON.stringify(self.ruleForm));
}
}).then((error) => {
console.log(error);
})
}
} else {
console.log('error submit!!');
return false;
}
});
},
gotoRegister() {
this.$router.push('/register');
},
randomNum(min, max) {
return Math.floor(Math.random() * (max - min) + min);
},
refreshCode() {
this.identifyCode = "";
this.makeCode(this.identifyCodes, 4);
},
makeCode(o, l) {
for (let i = 0; i < l; i++) {
this.identifyCode += this.identifyCodes[
this.randomNum(0, this.identifyCodes.length)
];
}
console.log(this.identifyCode);
},
}
}
</script>
<style scoped>
.login-wrap {
position: relative;
width: 100%;
height: 100%;
}
.ms-title {
position: absolute;
top: 50%;
width: 100%;
margin-top: -230px;
text-align: center;
font-size: 30px;
color: #fff;
}
.ms-login {
position: absolute;
left: 50%;
top: 50%;
width: 300px;
height: 240px;
margin: -150px 0 0 -190px;
padding: 40px;
border-radius: 5px;
background: #fff;
}
.ms-login span {
color: red;
}
.login-btn {
text-align: center;
}
.login-btn button {
width: 100%;
height: 36px;
}
.code {
width: 112px;
height: 35px;
border: 1px solid #ccc;
float: right;
border-radius: 2px;
}
.validate-code {
width: 136px;
float: left;
}
.register {
font-size: 14px;
line-height: 30px;
color: #999;
cursor: pointer;
float: right;
}
</style>

Identify.vue
<template>
<div class="s-canvas">
<canvas id="s-canvas" :width="contentWidth" :height="contentHeight"></canvas>
</div>
</template>
<script>
export default{
name: 'SIdentify',
props: {
identifyCode: {
type: String,
default: '1234'
},
fontSizeMin: {
type: Number,
default: 16
},
fontSizeMax: {
type: Number,
default: 40
},
backgroundColorMin: {
type: Number,
default: 180
},
backgroundColorMax: {
type: Number,
default: 240
},
colorMin: {
type: Number,
default: 50
},
colorMax: {
type: Number,
default: 160
},
lineColorMin: {
type: Number,
default: 40
},
lineColorMax: {
type: Number,
default: 180
},
dotColorMin: {
type: Number,
default: 0
},
dotColorMax: {
type: Number,
default: 255
},
contentWidth: {
type: Number,
default: 112
},
contentHeight: {
type: Number,
default: 38
}
},
methods: {
// 生成一个随机数
randomNum (min, max) {
return Math.floor(Math.random() * (max - min) + min)
},
// 生成一个随机的颜色
randomColor (min, max) {
let r = this.randomNum(min, max)
let g = this.randomNum(min, max)
let b = this.randomNum(min, max)
return 'rgb(' + r + ',' + g + ',' + b + ')'
},
drawPic () {
let canvas = document.getElementById('s-canvas')
let ctx = canvas.getContext('2d')
ctx.textBaseline = 'bottom'
// 绘制背景
ctx.fillStyle = this.randomColor(this.backgroundColorMin, this.backgroundColorMax)
ctx.fillRect(0, 0, this.contentWidth, this.contentHeight)
// 绘制文字
for (let i = 0; i < this.identifyCode.length; i++) {
this.drawText(ctx, this.identifyCode[i], i)
}
this.drawLine(ctx)
this.drawDot(ctx)
},
drawText (ctx, txt, i) {
ctx.fillStyle = this.randomColor(this.colorMin, this.colorMax)
ctx.font = this.randomNum(this.fontSizeMin, this.fontSizeMax) + 'px SimHei'
let x = (i + 1) * (this.contentWidth / (this.identifyCode.length + 1))
let y = this.randomNum(this.fontSizeMax, this.contentHeight - 5)
var deg = this.randomNum(-45, 45)
// 修改坐标原点和旋转角度
ctx.translate(x, y)
ctx.rotate(deg * Math.PI / 180)
ctx.fillText(txt, 0, 0)
// 恢复坐标原点和旋转角度
ctx.rotate(-deg * Math.PI / 180)
ctx.translate(-x, -y)
},
drawLine (ctx) {
// 绘制干扰线
for (let i = 0; i < 8; i++) {
ctx.strokeStyle = this.randomColor(this.lineColorMin, this.lineColorMax)
ctx.beginPath()
ctx.moveTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
ctx.lineTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
ctx.stroke()
}
},
drawDot (ctx) {
// 绘制干扰点
for (let i = 0; i < 100; i++) {
ctx.fillStyle = this.randomColor(0, 255)
ctx.beginPath()
ctx.arc(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight), 1, 0, 2 * Math.PI)
ctx.fill()
}
}
},
watch: {
identifyCode () {
this.drawPic()
}
},
mounted () {
this.drawPic()
}
}
</script>

三、后端代码

后端采用 SpringBoot
登录接口代码如下
@PostMapping(value = "/api/user/login", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Response login(@RequestBody User user,
HttpSession session) {
User u = userService.findByUserName(user.getName());
if (u == null) {
return Response.no("用户名不存在!");
}
if (!Objects.equals(u.getPassword(), user.getPassword())) {
return Response.no("密码不正确!");
}
session.setAttribute("session_user", u);
return Response.yes("登录成功", u);
}

User.java
package com.example.sens.entity;
import java.io.Serializable;
import java.util.Date;
/**
* <pre>
* 用户信息
* </pre>
*/
@Data
public class User implements Serializable {
private static final long serialVersionUID = -5144055068797033748L;
/**
* 用户ID
*/
private Long id;
/**
* 用户名
*/
private String name;
/**
* 密码
*/
private String password;
/**
* 姓名
*/
private String realName;
}

注意:@Data 是使用 lombok 的注解,如果您那边没有安装或引入lombok依赖,可以生成 getter/setter 来替代
Response.java
package com.example.sens.util;
import lombok.Data;
@Data
public class Response<T> {
private Integer code;
private String msg;
private T data;
public Response() {
}
public Response(Integer code) {
this.code = code;
}
public Response(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Response(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static <T> Response<T> yes() {
return new Response(0, "操作成功");
}
public static <T> Response<T> yes(String msg,T data) {
return new Response(0, msg, data);
}
public static <T> Response<T> yes(T data) {
return new Response(0, "操作成功", data);
}
public static <T> Response<T> no() {
return new Response(-1, "操作失败");
}
public static <T> Response<T> no(String msg) {
return new Response(-1, msg);
}
}

需要完整代码,可以联系博主


  • 微信
  • 交流学习,服务定制
  • weinxin
  • 个人淘宝
  • 店铺名:言曌博客咨询部

  • (部分商品未及时上架淘宝)
avatar

发表评论

avatar 登录者:匿名
匿名评论,评论回复后会有邮件通知

  

已通过评论:1   待审核评论数:0
  1. avatar 123

    博主大大 求求代码 救救孩子