广告
返回顶部
首页 > 资讯 > 后端开发 > JAVA >登录校验之滑块验证码完整实现(vue + springboot)
  • 432
分享到

登录校验之滑块验证码完整实现(vue + springboot)

vue.jsspringbootjava 2023-08-18 11:08:13 432人浏览 八月长安
摘要

文章目录 前言一、实现效果二、实现思路三、实现步骤1. 后端 java 代码1.1 新建一个拼图验证码类1.2 新建一个拼图验证码工具类1.3 新建一个 service 类1.4 新建一个 controller 类1.5 登录接口


前言

嗨,大家好,我是希留。

验证码一直是各类网站登录和注册的一种校验方式,是用来防止有人恶意使用脚本批量进行操作从而设置的一种安全保护方式。随着近几年技术的发展,人们对于系统安全性和用户体验的要求越来越高,大多数网站系统都逐渐采用行为验证码来代替传统的图片验证码。

今天这篇文章就来记录一下,我是如何实现从前端、到后端校验的整个流程的。


一、实现效果

无图无真相,实现的效果如下图所示,点击登录后弹出一个弹出层,拼图是由后端生成的,拖动滑块位置,后端校验是否已拖动到指定的位置。

在这里插入图片描述

二、实现思路

整体的实现思路如下:

  • 1、从服务器随机取一张底透明有形状的模板图,再随机取一张背景图、
  • 2、随机生成抠图块坐标
  • 3、根据步骤2的坐标点,对背景大图的抠图区域的颜色进行处理,新建的图像根据轮廓图颜色赋值,背景图生成遮罩层。
  • 4、完成以上步骤之后得到两张图(扣下来的方块图,带有抠图区域阴影的原图),将这两张图和抠图区域的y坐标传到前台,x坐标存入Redis
  • 5、前端在移动拼图时将滑动距离x坐标参数请求后台验证,服务器根据redis取出x坐标与参数的x进行比较,如果在伐值内则验证通过。如果滑动不成功,自动刷新图片,重置拼图,滑动成功,且账号密码正确就直接跳转到首页。

三、实现步骤

1. 后端 java 代码

1.1 新建一个拼图验证码类

代码如下(示例):

@Datapublic class Captcha {        private String nonceStr;        private String value;        private String canvasSrc;        private Integer canvasWidth;        private Integer canvasHeight;        private String blockSrc;        private Integer blockWidth;        private Integer blockHeight;        private Integer blockRadius;        private Integer blockX;        private Integer blockY;        private Integer place;}

1.2 新建一个拼图验证码工具

代码如下(示例):

public class CaptchaUtils {        private final static String IMG_URL = "https://loyer.wang/view/ftp/wallpaper/%s.jpg";        private final static String IMG_PATH = "E:/Temp/wallpaper/%s.jpg";        public static void checkCaptcha(Captcha captcha) {        //设置画布宽度默认值        if (captcha.getCanvasWidth() == null) {            captcha.setCanvasWidth(320);        }        //设置画布高度默认值        if (captcha.getCanvasHeight() == null) {            captcha.setCanvasHeight(155);        }        //设置阻塞块宽度默认值        if (captcha.getBlockWidth() == null) {            captcha.setBlockWidth(65);        }        //设置阻塞块高度默认值        if (captcha.getBlockHeight() == null) {            captcha.setBlockHeight(55);        }        //设置阻塞块凹凸半径默认值        if (captcha.getBlockRadius() == null) {            captcha.setBlockRadius(9);        }        //设置图片来源默认值        if (captcha.getPlace() == null) {            captcha.setPlace(0);        }    }        public static int getNonceByRange(int start, int end) {        Random random = new Random();        return random.nextInt(end - start + 1) + start;    }        public static BufferedImage getBufferedImage(Integer place) {        try {            //随机图片            int nonce = getNonceByRange(0, 1000);            //获取网络资源图片            if (0 == place) {                String imgUrl = String.fORMat(IMG_URL, nonce);                URL url = new URL(imgUrl);                return Imageio.read(url.openStream());            }            //获取本地图片            else {                String imgPath = String.format(IMG_PATH, nonce);                File file = new File(imgPath);                return ImageIO.read(file);            }        } catch (Exception e) {            System.out.println("获取拼图资源失败");            //异常处理            return null;        }    }        public static BufferedImage imageResize(BufferedImage bufferedImage, int width, int height) {        Image image = bufferedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);        BufferedImage resultImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);        Graphics2D graphics2D = resultImage.createGraphics();        graphics2D.drawImage(image, 0, 0, null);        graphics2D.dispose();        return resultImage;    }        public static void cutByTemplate(BufferedImage canvasImage, BufferedImage blockImage, int blockWidth, int blockHeight, int blockRadius, int blockX, int blockY) {        BufferedImage waterImage = new BufferedImage(blockWidth, blockHeight, BufferedImage.TYPE_4BYTE_ABGR);        //阻塞块的轮廓图        int[][] blockData = getBlockData(blockWidth, blockHeight, blockRadius);        //创建阻塞块具体形状        for (int i = 0; i < blockWidth; i++) {            for (int j = 0; j < blockHeight; j++) {                try {                    //原图中对应位置变色处理                    if (blockData[i][j] == 1) {                        //背景设置为黑色                        waterImage.setRGB(i, j, Color.BLACK.getRGB());                        blockImage.setRGB(i, j, canvasImage.getRGB(blockX + i, blockY + j));                        //轮廓设置为白色,取带像素和无像素的界点,判断该点是不是临界轮廓点                        if (blockData[i + 1][j] == 0 || blockData[i][j + 1] == 0 || blockData[i - 1][j] == 0 || blockData[i][j - 1] == 0) {blockImage.setRGB(i, j, Color.WHITE.getRGB());waterImage.setRGB(i, j, Color.WHITE.getRGB());                        }                    }                    //这里把背景设为透明                    else {                        blockImage.setRGB(i, j, Color.TRANSLUCENT);                        waterImage.setRGB(i, j, Color.TRANSLUCENT);                    }                } catch (ArrayIndexOutOfBoundsException e) {                    //防止数组下标越界异常                }            }        }        //在画布上添加阻塞块水印        addBlockWatermark(canvasImage, waterImage, blockX, blockY);    }        private static int[][] getBlockData(int blockWidth, int blockHeight, int blockRadius) {        int[][] data = new int[blockWidth][blockHeight];        double po = Math.pow(blockRadius, 2);        //随机生成两个圆的坐标,在4个方向上 随机找到2个方向添加凸/凹        //凸/凹1        int face1 = RandomUtils.nextInt(0,4);        //凸/凹2        int face2;        //保证两个凸/凹不在同一位置        do {            face2 = RandomUtils.nextInt(0,4);        } while (face1 == face2);        //获取凸/凹起位置坐标        int[] circle1 = getCircleCoords(face1, blockWidth, blockHeight, blockRadius);        int[] circle2 = getCircleCoords(face2, blockWidth, blockHeight, blockRadius);        //随机凸/凹类型        int shape = getNonceByRange(0, 1);        //圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆        //计算需要的小图轮廓,用二维数组来表示,二维数组有两张值,0和1,其中0表示没有颜色,1有颜色        for (int i = 0; i < blockWidth; i++) {            for (int j = 0; j < blockHeight; j++) {                data[i][j] = 0;                //创建中间的方形区域                if ((i >= blockRadius && i <= blockWidth - blockRadius && j >= blockRadius && j <= blockHeight - blockRadius)) {                    data[i][j] = 1;                }                double d1 = Math.pow(i - Objects.requireNonNull(circle1)[0], 2) + Math.pow(j - circle1[1], 2);                double d2 = Math.pow(i - Objects.requireNonNull(circle2)[0], 2) + Math.pow(j - circle2[1], 2);                //创建两个凸/凹                if (d1 <= po || d2 <= po) {                    data[i][j] = shape;                }            }        }        return data;    }        private static int[] getCircleCoords(int face, int blockWidth, int blockHeight, int blockRadius) {        //上        if (0 == face) {            return new int[]{blockWidth / 2 - 1, blockRadius};        }        //左        else if (1 == face) {            return new int[]{blockRadius, blockHeight / 2 - 1};        }        //下        else if (2 == face) {            return new int[]{blockWidth / 2 - 1, blockHeight - blockRadius - 1};        }        //右        else if (3 == face) {            return new int[]{blockWidth - blockRadius - 1, blockHeight / 2 - 1};        }        return null;    }        private static void addBlockWatermark(BufferedImage canvasImage, BufferedImage blockImage, int x, int y) {        Graphics2D graphics2D = canvasImage.createGraphics();        graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.8f));        graphics2D.drawImage(blockImage, x, y, null);        graphics2D.dispose();    }        public static String toBase64(BufferedImage bufferedImage, String type) {        try {            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();            ImageIO.write(bufferedImage, type, byteArrayOutputStream);            String base64 = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());            return String.format("data:image/%s;base64,%s", type, base64);        } catch (IOException e) {            System.out.println("图片资源转换BASE64失败");            //异常处理            return null;        }    }}

1.3 新建一个 service 类

代码如下(示例):

@Servicepublic class CaptchaService {        private static Integer ALLOW_DEVIATION = 3;    @Autowired    private StringRedisTemplate stringRedisTemplate;        public String checkImageCode(String imageKey, String imageCode) {        ValueOperations ops = stringRedisTemplate.opsForValue();        String text = ops.get("imageCode:" + imageKey);        if(StrUtil.isBlank(text)){            return "验证码已失效";        }        // 根据移动距离判断验证是否成功        if (Math.abs(Integer.parseInt(text) - Integer.parseInt(imageCode)) > ALLOW_DEVIATION) {            return "验证失败,请控制拼图对齐缺口";        }        return null;    }        public void saveImageCode(String key, String code) {        ValueOperations ops = stringRedisTemplate.opsForValue();        ops.set("imageCode:" + key, code, 15, TimeUnit.MINUTES);    }        public Object getCaptcha(Captcha captcha) {        //参数校验        CaptchaUtils.checkCaptcha(captcha);        //获取画布的宽高        int canvasWidth = captcha.getCanvasWidth();        int canvasHeight = captcha.getCanvasHeight();        //获取阻塞块的宽高/半径        int blockWidth = captcha.getBlockWidth();        int blockHeight = captcha.getBlockHeight();        int blockRadius = captcha.getBlockRadius();        //获取资源图        BufferedImage canvasImage = CaptchaUtils.getBufferedImage(captcha.getPlace());        //调整原图到指定大小        canvasImage = CaptchaUtils.imageResize(canvasImage, canvasWidth, canvasHeight);        //随机生成阻塞块坐标        int blockX = CaptchaUtils.getNonceByRange(blockWidth, canvasWidth - blockWidth - 10);        int blockY = CaptchaUtils.getNonceByRange(10, canvasHeight - blockHeight + 1);        //阻塞块        BufferedImage blockImage = new BufferedImage(blockWidth, blockHeight, BufferedImage.TYPE_4BYTE_ABGR);        //新建的图像根据轮廓图颜色赋值,源图生成遮罩        CaptchaUtils.cutByTemplate(canvasImage, blockImage, blockWidth, blockHeight, blockRadius, blockX, blockY);        // 移动横坐标        String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");        // 缓存        saveImageCode(nonceStr,String.valueOf(blockX));        //设置返回参数        captcha.setNonceStr(nonceStr);        captcha.setBlockY(blockY);        captcha.setBlockSrc(CaptchaUtils.toBase64(blockImage, "png"));        captcha.setCanvasSrc(CaptchaUtils.toBase64(canvasImage, "png"));        return captcha;    }}

1.4 新建一个 controller 类

代码如下(示例):

@RestController@RequestMapping("/captcha")public class CaptchaController {    @Autowired    private CaptchaService captchaService;    @apiOperation(value = "生成验证码拼图")    @PostMapping("get-captcha")    public R getCaptcha(@RequestBody Captcha captcha) {        return R.ok(captchaService.getCaptcha(captcha));    }}

1.5 登录接口

代码如下(示例):

    @ApiOperation(value = "登录")    @PostMapping(value = "login")    public R login(@RequestBody LoginVo loginVo) {        // 只有开启了验证码功能才需要验证        if (needAuthCode) {            String msg = captchaService.checkImageCode(loginVo.getNonceStr(),loginVo.getValue());            if (StringUtils.isNotBlank(msg)) {                return R.error(msg);            }        }        String token = loginService.login(loginVo.getUserName(), loginVo.getPassWord());        if (StringUtils.isBlank(token)) {            return R.error("用户名或密码错误");        }        Map tokenMap = new HashMap<>();        tokenMap.put("token", token);        tokenMap.put("tokenHead", tokenHead);        return R.ok(tokenMap);    }

2. 前端 Vue 代码

2.1 新建一个 sliderVerify 组件

代码如下(示例):

2.2 在登录页使用滑块组件

代码如下(示例):

      

总结

好了,以上就是本文的全部内容了,感谢您的阅读。

若觉得本文对你有帮助的话,还不忘点赞评论支持一下,感谢~

来源地址:https://blog.csdn.net/qq_30859353/article/details/126747608

--结束END--

本文标题: 登录校验之滑块验证码完整实现(vue + springboot)

本文链接: https://www.lsjlt.com/news/373874.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 登录校验之滑块验证码完整实现(vue + springboot)
    文章目录 前言一、实现效果二、实现思路三、实现步骤1. 后端 java 代码1.1 新建一个拼图验证码类1.2 新建一个拼图验证码工具类1.3 新建一个 service 类1.4 新建一个 controller 类1.5 登录接口 ...
    99+
    2023-08-18
    vue.js spring boot java
  • SpringBoot实现滑块验证码验证登陆校验功能详解
    目录前言一、实现效果二、实现思路三、实现步骤1. 后端 java 代码1.1 新建一个拼图验证码类1.2 新建一个拼图验证码工具类1.3 新建一个 service 类1.4 新建一个...
    99+
    2022-11-13
  • vue实现登录时滑块验证
    本文实例为大家分享了vue实现登录时滑块验证的具体代码,供大家参考,具体内容如下 1.效果图 2. 新建 SliderCheck.vue组件 <template> &...
    99+
    2022-11-13
  • vue+springboot实现登录验证码
    本文实例为大家分享了vue+springboot实现登录验证码的具体代码,供大家参考,具体内容如下 先看效果图 在login页面添加验证码html 在后端pom文件添加kaptc...
    99+
    2022-11-12
  • JavaScript实现登录滑块验证
    本文实例为大家分享了JavaScript实现登录滑块验证的具体代码,供大家参考,具体内容如下 html代码 <div class="login-select"> ...
    99+
    2022-11-12
  • vue实现token登录验证的完整实例
    目录token可用于登录验证和权限管理。登录页 -----Login.vue路由守卫 ----- router/index.js封装axios 添加请求拦截器 在每次请求之前进行的操...
    99+
    2022-11-13
  • vue+springboot如何实现登录验证码
    这篇文章主要介绍vue+springboot如何实现登录验证码,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!先看效果图在login页面添加验证码html在后端pom文件添加kaptcha依赖<dependenc...
    99+
    2023-06-15
  • vue实现登录验证码
    本文实例为大家分享了vue实现登录验证码的具体代码,供大家参考,具体内容如下 先来demo效果图 canvas验证码组件(可直接复制,无需改动) <template>...
    99+
    2022-11-12
  • vue实现登录滑动拼图验证
    本文实例为大家分享了vue实现登录滑动拼图验证的具体代码,供大家参考,具体内容如下 一、安装插件 npm install --save vue-monoplasty-slide-ve...
    99+
    2022-11-13
  • selenium+opencv实现滑块验证码的登陆
    目录环境selenium登录网站requests抓取验证码图片OpenCV识别缺口位置模拟拖动滑块脚本示例:很多网站登录登陆时都要用到滑块验证码,在某些场景例如使用爬虫爬取信息时常常...
    99+
    2023-05-15
    selenium opencv滑块验证码 opencv滑块验证码
  • springboot整合shiro多验证登录功能的实现(账号密码登录和使用手机验证码登录)
    1. 首先新建一个shiroConfig shiro的配置类,代码如下: @Configuration public class SpringShiroConfig { ...
    99+
    2022-11-12
  • Springboot整合Shiro怎么实现登录与权限校验
    这篇文章主要介绍“Springboot整合Shiro怎么实现登录与权限校验”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Springboot整合Shiro怎么实现登录与权限校验”文章能帮助大家解决问...
    99+
    2023-06-30
  • Vue如何实现验证码登录
    本文小编为大家详细介绍“Vue如何实现验证码登录”,内容详细,步骤清晰,细节处理妥当,希望这篇“Vue如何实现验证码登录”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。效果展示第一步:创建验证码组件这里是组件的代码...
    99+
    2023-06-29
  • vue实现图形验证码登录
    本文实例为大家分享了vue实现图形验证码登录的具体代码,供大家参考,具体内容如下 1、效果图 2、在components下面新建文件identify.vue,内容: <t...
    99+
    2022-11-12
  • vue实现手机验证码登录
    本文实例为大家分享了vue实现手机验证码登录的具体代码,供大家参考,具体内容如下 验证码 <template> <div> <el-ma...
    99+
    2022-11-12
  • vue实现登录时图形验证码
    本文实例为大家分享了vue实现登录时图形验证码的具体代码,供大家参考,具体内容如下 效果图: 点击图案可以切换字符 1.新建 Identify.vue 组件 <templat...
    99+
    2022-11-13
  • vue+Element实现登录随机验证码
    本文实例为大家分享了vue+Element实现登录随机验证码的具体代码,供大家参考,具体内容如下 验证码验证只是前端,无需后台交互 首先,创建一个identify.vue页面,用于写...
    99+
    2022-11-13
  • Springboot整合Shiro实现登录与权限校验详细解读
    目录Springboot-cli 开发脚手架系列简介前言1. 环境2. 简介3. Realm配置4. 核心配置5. 接口编写6. 网页资源7. 效果演示8. 源码分享Springbo...
    99+
    2022-11-13
  • Springboot+SpringSecurity怎么实现图片验证码登录
    本文小编为大家详细介绍“Springboot+SpringSecurity怎么实现图片验证码登录”,内容详细,步骤清晰,细节处理妥当,希望这篇“Springboot+SpringSecurity怎么实现图片验证码登录”文章能帮助大家解决疑惑...
    99+
    2023-06-30
  • springboot怎么整合shiro实现多验证登录功能
    这篇“springboot怎么整合shiro实现多验证登录功能”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“springbo...
    99+
    2023-06-08
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作