Spring Boot实现验证码功能

本文涉及的产品
函数计算FC,每月15万CU 3个月
简介: Spring Boot实现验证码功能

验证码的作用

验证码的作用:可以有效防止其他人对某一个特定的注册用户用特定的程序暴力破解方式进行不断的登录尝试
我们其实很经常看到,登录一些网站其实是需要验证码的,比如牛客,QQ等。使用验证码是现在很多网站通行的一种方式,这个问题是由计算机生成并且评判的,但是必须只有人类才能解答,因为计算机无法解答验证码的问题,所以回答出问题的用户就可以被认为是人类。
验证码一般用来防止批量注册。

案例要求

验证码本质:后端程序随机验证码
图片的创建:
---java api手动创建图片
---JavaScript 前端创建图片
验证码的刷新
---添加JavaScript的点击事件,重新请求验证码图片

前端页面准备

因为涉及到jQuery,所以需要在resources/static创建目录,存放jQuery库
在这里插入图片描述

准备login.html页面

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>用户登录</title>
    <!--引入jQuery -->
    <script type="text/javascript" src="/js/jquery-1.8.3.min.js"></script>
  </head>
  <body>
    <h2>用户登录</h2>
    <form action="/login" method="post">
      用户名:<input type="text" name="username"><br><br>
      密码  :<input type="password" name="password"><br><br>
      验证码:<input id="identify-input" type="text" name="identifyCode">
      <img id="identify-img" src="/identifyImage"><br><br>
      <input type="submit" value="登录">
    </form>
    <!--绑定点击事件 -->
    <script>
      $("#identify-img").on('click',function (){
        // 点击验证码那个图片的时候,我们输入的验证码那个框就会清空
        $('#identify-input').val('')
        //而且我们点击验证码的时候,希望它可以改变验证码内容,其实是通过发送新请求来改变验证码内容
        $('#identify-img').attr('src','/identifyImage?'+Math.random())
      })
      
    </script>
  </body>
</html>

随机验证码工具类

public class IdentifyCodeUtils {
    //设置图片宽
    private int width = 95;
    //设置图片高度
    private int height = 25;
    //设置干扰线数量
    private int lineSize = 40;
    //随机产生数字和字母组合的字符串
    private String randString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private Random random = new Random();
    
    /**
    * 获得字体
    */
    private Font getFont() {
        return new Font("Fixedsys", Font.CENTER_BASELINE, 18);
    }
    
    /**
    * 获得颜色
    */
    private Color getRandColor(int fc, int bc) {
        if (fc > 255) {
            fc = 255;
        }
        if (bc > 255) {
            bc = 255;
        }
        int r = fc + random.nextInt(bc - fc - 16);
        int g = fc + random.nextInt(bc - fc - 14);
        int b = fc + random.nextInt(bc - fc - 18);
        return new Color(r, g, b);
    }
    
    /**
    * 获取验证码
    *
    * @return
    */
    public String getIdentifyCode() {
        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < 4; i++) {
            char c = randString.charAt(random.nextInt(randString.length()));
            buffer.append(c);
        }
        return buffer.toString();
    }
    
    /**
    * 生成随机图片
    *
    * @param identifyCode
    * @return
    */
    public BufferedImage getIdentifyImage(String identifyCode) {
        //BufferedImage类是具有缓冲区的Image类,Image类是用来描述图像信息的类
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
        //产生Image对象的Graphics对象,改对象可以在图像上进行各种绘制操作
        Graphics graphics = image.getGraphics();
        //图片大小
        graphics.fillRect(0, 0, width, height);
        //字体大小
        graphics.setFont(new Font("Times New Roman", Font.ROMAN_BASELINE, 18));
        //字体颜色
        graphics.setColor(getRandColor(110, 133));
        //绘制干扰线
        for (int i = 0; i <= lineSize; i++) {
            drawLine(graphics);
        }
        //绘制随机字符
        drawString(graphics, identifyCode);
        graphics.dispose();
        return image;
        
    }
    
    /**
    * 绘制字符串
    */
    private void drawString(Graphics g, String identifyCode) {
        for (int i = 0; i < identifyCode.length(); i++) {
            g.setFont(getFont());
            g.setColor(new Color(random.nextInt(101), random.nextInt(111), random
                                 .nextInt(121)));
            g.translate(random.nextInt(3), random.nextInt(3));
            g.drawString(String.valueOf(identifyCode.charAt(i)), 13 * i + 20, 18);
        }
        
        
    }
    
    /**
    * 响应验证码图片
    *
    * @param identifyImg
    * @param response
    */
    public void responseIdentifyImg(BufferedImage identifyImg, HttpServletResponse response) {
        //设置响应类型,告诉浏览器输出的内容是图片
        response.setContentType("image/jpeg");
        //设置响应头信息,告诉浏览器不用缓冲此内容
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expire", 0);
        try {
            //把内存中的图片通过流动形式输出到客户端
            ImageIO.write(identifyImg, "JPEG", response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    
    /**
    * 绘制干扰线
    */
    private void drawLine(Graphics graphics) {
        int x = random.nextInt(width);
        int y = random.nextInt(height);
        int xl = random.nextInt(13);
        int yl = random.nextInt(15);
        graphics.drawLine(x, y, x + xl, y + yl);
    }
    
    
    
    
}

后端控制器

@Controller
public class UserController {
    @RequestMapping("/loginShow")
    public String loginShow(){
        return "login.html";
    }
    @PostMapping("/login")
    public String login(String username,String password,String identifyCode,HttpSession session){
        System.out.println("用户名:"+username);
        System.out.println("密码:"+password);
        System.out.println("验证码:"+identifyCode);
        //从session中取出验证码
        String sessionCode = (String)session.getAttribute("identifyFyCode");
        if (identifyCode.equalsIgnoreCase(sessionCode)){
            System.out.println("验证码正确");
            //进行登录判断的逻辑大家自己写,这里就不演示了
        }else{
            System.out.println("验证码错误");
            //重定向到登录画面
            return "redirect:/loginShow";
        }
         return "";
    }

    /**
     * 给前端返回一个验证码图片
     * @return
     */
    @RequestMapping("/identifyImage")
    public void identifyImage(HttpServletResponse response, HttpSession session){
        //创建随机验证码
        IdentifyCodeUtils utils = new IdentifyCodeUtils();
        String identifyCode = utils.getIdentifyCode();
        //session存入验证码
        session.setAttribute("identifyCode", identifyCode);
        //根据验证码创建图片
        BufferedImage identifyImage = utils.getIdentifyImage(identifyCode);
        //回传给前端
        utils.responseIdentifyImg(identifyImage,response);

    }
}

测试

在这里插入图片描述

当我们点击验证码这个图片的时候,它就会生成新验证码
并且如果我们在输入框中如果有写验证码的话,当我们点击验证码图片,它就会把输入框内容清空(大家自己测试)

相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
相关文章
|
27天前
|
XML 安全 Java
|
1月前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
61 0
|
5天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
27天前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
53 14
|
1月前
|
消息中间件 缓存 Java
手写模拟Spring Boot启动过程功能
【11月更文挑战第19天】Spring Boot自推出以来,因其简化了Spring应用的初始搭建和开发过程,迅速成为Java企业级应用开发的首选框架之一。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,帮助读者深入理解其工作机制。
46 3
|
1月前
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
45 2
|
1月前
|
前端开发 Java easyexcel
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
125 8
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
102 2
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
245 2
|
5天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)