项目中登录验证码怎么做才合理

news2024/7/7 17:16:11

唠嗑部分

今天我们来聊聊项目实战中登录验证码如何做比较合理,首先我们聊以下几个问题

1、登录时验证码校验是否必要?

答案当然是很有必要的,因为用户登录行为会直接影响数据库,如果没有某些防范措施,有恶意用户暴力破解密码,那就十分严重了,验证码校验是防止接口被刷的一种方式,可以大大的降低接口请求的频率

还有一些系统会加入密码次数校验,当密码错误次数超过n次时,会锁定该用户,在指定时间段内不允许登录

除此之外,对于用户接口限流也是一种方案,小伙伴可以自行尝试

2、验证码校验是前端行为还是服务端行为?

两种方式均可以,区别如下

前端行为:仅能保证前端页面操作时验证的效果,不能够保证接口安全,用户可跳过页面直接访问服务端接口,不建议

服务端行为:能够保证登陆接口安全,限制用户登录频率,可配合用户ip流量限制、密码错误次数防范等措施,建议

3、生成验证码后,服务器应该存在什么地方,会有哪些问题?

在学习jsp+servlet时,用户信息通常会存入到服务端的session当中,验证码也可存入session,但是之针对单实例可行,如多实例,需实现session共享或配合ip_hash的规则

可存入分布式缓存中,不会有多实例问题、也不需要解决session共享的问题

4、如何实现分布式验证码校验,保证登录安全?

首先,服务端提供获取验证码的接口,出参包含key、code(code为实际验证码,key为Redis存储的key),返回验证码之前将验证码存入redis中

前端收到验证码后,采用canvas绘制验证码,用户手动输入验证码后,将用户名、密码、验证码、key传入服务端进行登录

服务端收到登录请求后,查询redis,跟用户输入的验证码进行比较,比较通过后进行登录

言归正传

验证码校验是用户登录的第一道屏障,未验证通过则不进行登录,不接触数据库

1、导入验证码依赖,其他依赖自行添加,需redis、hutool-all

<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>

2、配置kaptcha

/*
 * @Project:cxs-currency-sys-server
 * @Author:cxs
 * @Motto:放下杂念,只为迎接明天更好的自己
 * */
@Configuration
public class KaptchaConfig {
    
    private static final String CHAR_STR = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    private static final String CHAR_LENGTH = "4";

    @Bean
    public Producer captcha() {
        // 配置图形验证码的基本参数
        Properties properties = new Properties();
        // 字符集
        properties.setProperty("kaptcha.textproducer.char.string", CHAR_STR);
        // 字符长度
        properties.setProperty("kaptcha.textproducer.char.length", CHAR_LENGTH);
        Config config = new Config(properties);
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

3、生成验证码核心代码

public void getValidateCode(BaseResult result) {
        long startTime = System.currentTimeMillis();
        try {
            // 创建验证码文本
            ValidateCodeVO vo = new ValidateCodeVO();
            String key = IdUtil.simpleUUID();
            String code = captchaProducer.createText();
            redisUtil.set(redisUtil.getCacheKey(CachePrefixContent.VALIDATE_CODE_PREFIX, key), code, commonConfig.getCodePeriod(), TimeUnit.SECONDS);
            vo.setKey(key);
            vo.setCode(code);
            result.setData(vo);
        } catch (Exception e) {
            log.error("生成验证码失败,{}", e);
            result.setCode(CurrencyErrorEnum.OPERA_ERROR.getCode());
            result.setMsg(CurrencyErrorEnum.OPERA_ERROR.getMsg());
        } finally {
            long endTime = System.currentTimeMillis();
            log.info("【{}】【生成验证码接口】【{}ms】 \n入参:{}\n出参:{}", "生成验证码", endTime - startTime, null, result);
        }
    }

4、接口出参预览

{
  "code": 200,
  "data": {
    "code": "2972",
    "key": "a23030e821384684a47912fac215cfb1"
  },
  "msg": "操作成功"
}

5、前端实现主要代码(注意:登录框的部分代码)

<el-form-item prop="code">
    <el-row :gutter="20">
        <el-col :span="12">
            <el-input ref="code" type="text" prefix-icon="el-icon-key" v-model="userInfo.code" placeholder="验证码" name="code" tabindex="2" autocomplete="on" @keyup.enter.native="loginHandle"/>
         </el-col>
    	<el-col :span="6" :offset="3">
        	<canvas ref="canvas" :width="codeInfo.codeWidth" :height="codeInfo.codeHeight" @click="reloadCode"></canvas>
    	</el-col>
	</el-row>
</el-form-item>

6、需要到的函数

// 创建验证码
async createdCode() {
    // 调用上述接口生成
      const res = await getValidateCode()
      const {code, data, msg} = res
      this.userInfo.codeKey = data.key
      const identifyCode = data.code
      const codeList = identifyCode.split("");
      const canvas = this.$refs.canvas;// 获取 canvas 元素
      const ctx = canvas.getContext('2d');
      ctx.textBaseline = 'bottom';
      if (this.backgroundColor != '' && this.backgroundColor != null) {// 绘制画布背景颜色
        ctx.fillStyle = this.backgroundColor;
      } else {
        ctx.fillStyle = this.randomColor(255, 255);
      }
      ctx.fillRect(0, 0, this.contentWidth, this.contentHeight);
      codeList.forEach((code, i) => {// 绘制验证码字符
        this.drawText(ctx, code, i + 1, identifyCode.length);
      })
      this.drawLine(ctx);// 绘制干扰线
      this.drawDot(ctx);// 绘制干扰点
    },
    randomNum(min, max) {// 生成指定范围内的随机整数
      return Math.floor(Math.random() * (max - min) + min);
    },
    randomColor(min, max) {// 生成指定范围内的随机颜色
      const r = this.randomNum(min, max);
      const g = this.randomNum(min, max);
      const b = this.randomNum(min, max);
      return `rgb(${r},${g},${b})`;
    },
    drawText(ctx, txt, i, len) {// 绘制验证码字符
      ctx.fillStyle = this.randomColor(0, 160);
      ctx.font = `${this.randomNum(25, 30)}px SimHei`;
      const x = (i / (len + 1)) * 120;
      const y = this.randomNum(30, 35);
      const 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 < 5; i++) {
        ctx.strokeStyle = this.randomColor(100, 255);
        ctx.beginPath();
        ctx.moveTo(this.randomNum(0, 120), this.randomNum(0, 40));
        ctx.lineTo(this.randomNum(0, 120), this.randomNum(0, 40));
        ctx.stroke();
      }
    },
    drawDot(ctx) {// 绘制干扰点
      for (let i = 0; i < 80; i++) {
        ctx.fillStyle = this.randomColor(0, 255);
        ctx.beginPath();
        ctx.arc(this.randomNum(0, 120), this.randomNum(0, 40), 1, 0, 2 * Math.PI);
        ctx.fill();
      }
    },
    reloadCode() {// 点击按钮时清除画布并重新生成验证码
      const canvas = this.$refs.canvas
      const ctx = canvas.getContext('2d')
      ctx.clearRect(0, 0, canvas.width, canvas.height)
      this.createdCode()
    },

7、效果展示

image-20231104161529694

结语

1、验证码功能就实现了,快去给你的项目安排吧!

2、制作不易,一键三连再走吧,您的支持永远是我最大的动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1178813.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NOIP2023模拟12联测33 A. 构造

NOIP2023模拟12联测33 A. 构造 文章目录 NOIP2023模拟12联测33 A. 构造题目大意思路code 题目大意 构造题 思路 想一种构造方法&#xff0c;使得 y y y 能够凑成尽可能多的答案 第一行 x y r y ⋯ r xyry \cdots r xyry⋯r 第二行 r y x y ⋯ x ryxy \cdots x ryxy⋯x …

基于SSM的出租车管理系统

基于SSM的出租车管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringSpringMVCMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 登录界面 管理员界面 驾驶员界面 摘要 基于SSM&#xff08;Spring、Spring MVC、My…

软考 -- 计算机学习(3)

文章目录 一、软件测试基础1.1 基本概念1.2 软件测试模型1.3 软件测试的分类 二、基于规格说明的测试技术(黑盒)2.1 重要的测试方法1. 等价类划分法2. 边界值法3. 判定表法4. 因果图法 2.2 其他测试方法 三、基于结构的测试技术(白盒)3.1 静态测试3.2 动态测试 一、软件测试基础…

Vue Vuex模块化编码

正常写vuex的index的时候如果数据太多很麻烦&#xff0c;如有的模块是管理用户信息或修改课程等这两个是不同一个种类的&#xff0c;如果代码太多会造成混乱&#xff0c;这时候可以使用模块化管理 原始写法 如果功能模块太多很乱 import Vue from vue import Vuex from vuex …

nodejs卸载和安装教程

一、卸载 1、Win菜单中找到Node.js的卸载程序&#xff0c;运行卸载程序。 3.选择 OK&#xff0c;等待卸载。 4. 删除C:\Users\用户名\AppData\Roaming目录下的npm和npm-cache&#xff1b;删除C:\Users\123\AppData\Local\目录下的npm-cache。 二、安装 傻瓜式安装&#xf…

socket开发步骤及相关API介绍

socket服务器和客户端的开发步骤 TCP服务端&#xff1a; 创建套接字socket为套接字添加信息&#xff08;IP地址和端口号&#xff09;bind监听网络连接listen监听到由客户端接入&#xff0c;接受一个连接accept数据交互read、write关闭套接字&#xff0c;断开连接close TCP客户…

JAVA二叉搜索树(专门用来查找)

目录 二叉搜索树又叫二叉排序树&#xff0c;它具有以下特征 二次搜索树的效率 模拟最简二叉搜索树代码 代码片段分析 查找二叉搜索树数据&#xff1a; 如果我们用递归的方法查找数据有什么不一样? 插入数据 删除数据(难点) 二叉搜索树又叫二叉排序树&#xff0c;它具有以下特征…

python之pyQt5实例:几何绘图界面

使用PyQt5设计一个界面&#xff0c;其中点击不同的按钮可以在画布上画出点、直线、圆和样条曲线 from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton,QHBoxLayout,QVBoxLayout,QWidget,QLabel from PyQt5.QtGui import QPainter, QPen, QColor from PyQt5.Q…

nssm将exe应用封装成windows服务

一、简介 NSSM&#xff08;Non-Sucking Service Manager&#xff09;是一个用于在Windows操作系统上管理和运行应用程序作为服务的工具。它提供了一种简单的方法来将任意可执行文件转换为Windows服务&#xff0c;并提供了一些额外的功能和配置选项。 优点&#xff1a; 简单易…

【遍历二叉树算法描述】

文章目录 遍历二叉树算法描述先序遍历二叉树的操作定义中序遍历二叉树的操作定义后序遍历二叉树的操作定义 遍历二叉树算法描述 1.遍历定义&#xff1a;顺着某一条搜索路径寻访二叉树中的结点&#xff0c;使得每一个结点均被访问一次&#xff0c;而且仅访问一次&#xff08;又…

【算法-数组3】螺旋数组(一入循环深似海啊!)

今天&#xff0c;带来数组相关算法的讲解。文中不足错漏之处望请斧正&#xff01; 理论基础点这里 螺旋数组 1. 思路 这道题主要是模拟转圈过程&#xff0c;但是要处理的边界条件比较多&#xff0c;常见的问题就是每条边的处理都有自己的逻辑&#xff0c;那这就很难。如果不…

基于正负序双dq旋转坐标系锁相环 DDSRF-PLL模型

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; DDSRF-PLL则是通过构建数学解耦网络来消除&#xff12;倍电网频率的交流耦合分量 。由于DDSRF-PLL是在解耦多同步坐标系锁相环的基础上得到的&#xff0c;因此&#xff0c;需要研究解耦多同步坐标系锁相环的组…

高速串行总线—Rapid IO

SRIO简介 Rapid IO 是一种高性能、 低引脚数、 基于数据包交换的互连体系结构&#xff0c;是为满足和未来高性能嵌入式系统需求而设计的一种开放式互连技术标准。RapidIO主要应用于嵌入式系统内部互连&#xff0c;支持芯片到芯片、板到板间的通讯&#xff0c;可作为嵌入式设备的…

第26期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大型语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以…

Vue-router 路由的基本使用

Vue-router是一个Vue的插件库&#xff0c;专门用于实现SPA应用&#xff0c;也就是整个应用是一个完整的页面&#xff0c;点击页面上的导航不会跳转和刷新页面。 一、安装Vue-router npm i vue-router // Vue3安装4版本 npm i vue-router3 // Vue2安装3版本 二、引入…

什么是数据可视化,为什么数据可视化很重要?

数据可视化是数据的图形表示&#xff0c;可以帮助人们更轻松地理解和解释复杂的信息。它涉及创建数据的视觉表示&#xff0c;例如图表、图形、地图和其他视觉元素&#xff0c;以传达数据中的见解、模式和趋势。数据可视化是将原始数据转化为可操作知识的关键工具。 以下是数据…

CSS 背景、文本、字体

CSS背景&#xff1a; CSS背景属性用于定义HTML元素的背景。CSS属性定义背景效果&#xff1a;background-color&#xff1b;background-image&#xff1b;background-repeat&#xff1b;background-attachment&#xff1b;background-position。 background-color属性定义元素…

swift语言用哪种库适合做爬虫?

因为Swift语言并没有在语言层面上支持正则表达式&#xff0c;这对于爬虫来说是一个很大的缺陷。不过&#xff0c;Swift语言可以通过调用其他语言的库来实现爬虫功能&#xff0c;比如可以使用Python的BeautifulSoup库或者JavaScript的Cheerio库来解析HTML页面。但是相比于Python…

【Proteus仿真】【51单片机】汽车尾灯控制设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使用按键、LED模块等。 主要功能&#xff1a; 系统运行后&#xff0c;系统运行后&#xff0c;系统开始运行&#xff0c;K1键控制左转向灯&#xff1b;…

第12章 PyTorch图像分割代码框架-2

模型模块 本书的第5-9章重点介绍了各种2D和3D的语义分割和实例分割网络模型&#xff0c;所以在模型模块中&#xff0c;我们需要做的事情就是将要实验的分割网络写在该目录下。有时候我们可能想尝试不同的分割网络结构&#xff0c;所以在该目录下可以存在多个想要实验的网络模型…