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

avatar 2020年05月18日14:33:18 0 948 views
周末帮一个朋友写了一个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
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
avatar

发表评论

avatar 登录者:匿名
您需要登录才能评论,可以选择注册或者QQ快速登录

     

已通过评论:0   待审核评论数:0