生成验证码图片

引入依赖包

XML 复制代码
<!-- 图形验证码 -->
<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>

配置DefaultKaptcha类

类方法配置了验证码的生成格式和规则,并返回一个DefaultKaptcha对象并注入到Spring中

java 复制代码
package com.dream.datacenter.config;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.Properties;

/**
 * Created with IntelliJ IDEA
 * 图形验证码属性配置
 * @Author: Mr.HPC
 * @Date: 2022/1/6 23:08
 * @Version 1.0
 */
@Component
public class CodeImgConfig {
    @Bean
    public DefaultKaptcha getDefaultKaptcha() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        // 图片边框
        properties.setProperty("kaptcha.border", "no");
        // 边框颜色
        properties.setProperty("kaptcha.border.color", "black");
        //边框厚度
        properties.setProperty("kaptcha.border.thickness", "1");
        // 图片宽
        properties.setProperty("kaptcha.image.width", "200");
        // 图片高
        properties.setProperty("kaptcha.image.height", "50");
        //图片实现类
        properties.setProperty("kaptcha.producer.impl", "com.google.code.kaptcha.impl.DefaultKaptcha");
        //文本实现类
        properties.setProperty("kaptcha.textproducer.impl", "com.google.code.kaptcha.text.impl.DefaultTextCreator");
        //文本集合,验证码值从此集合中获取
        properties.setProperty("kaptcha.textproducer.char.string", "01234567890");
        //验证码长度
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        //字体
        properties.setProperty("kaptcha.textproducer.font.names", "宋体");
        //字体颜色
        properties.setProperty("kaptcha.textproducer.font.color", "black");
        //文字间隔
        properties.setProperty("kaptcha.textproducer.char.space", "5");
        //干扰实现类
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.DefaultNoise");
        //干扰颜色
        properties.setProperty("kaptcha.noise.color", "blue");
        //干扰图片样式
        properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");
        //背景实现类
        properties.setProperty("kaptcha.background.impl", "com.google.code.kaptcha.impl.DefaultBackground");
        //背景颜色渐变,结束颜色
        properties.setProperty("kaptcha.background.clear.to", "white");
        //文字渲染器
        properties.setProperty("kaptcha.word.impl", "com.google.code.kaptcha.text.impl.DefaultWordRenderer");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

Controller 请求方法

java 复制代码
package com.dream.datacenter.controller;


import com.dream.datacenter.shiro.JWTUtil;
import com.dream.datacenter.utils.ImageUtil;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/test")
public class testController {

    public static final String VERIFICATION_CODE = "verificationCode_";

    @Autowired
    public RedisTemplate redisTemplate;


    @Resource
    private DefaultKaptcha captcha;

    /**
     * 第一种方式
     * 使用DefaultKaptcha生成登录验证码图片
     * 转成base64编码的字符串输出到前端
     * 实际项目这里还要加入JWT令牌校验和redis缓存
     */
    @RequestMapping(value = {"/loginValidateCode"})
    public String loginValidateCode(HttpServletRequest request, HttpServletResponse response) throws Exception{
        response.setDateHeader("Expires", 0);
        // Set standard HTTP/1.1 no-cache headers.
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        // Set IE extended HTTP/1.1 no-cache headers (use addHeader).
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        // Set standard HTTP/1.0 no-cache header.
        response.setHeader("Pragma", "no-cache");
        // return a jpeg
        response.setContentType("image/jpeg");

        // 生成图片验证码内容
        String capText = captcha.createText();
        // 获取用户的jwt令牌
        String userVerificationJwt = request.getHeader(JWTUtil.JWT_VERIFICATION_KEY);
        //验证码令牌
        Claims claims = JWTUtil.validateJwtToken(userVerificationJwt);
        if(claims == null){
            //如果用户令牌过期那么对应存放在redis中的数据也要清空
            if(!org.apache.commons.lang3.StringUtils.isEmpty(userVerificationJwt)){
                redisTemplate.expire(VERIFICATION_CODE + userVerificationJwt, 1, TimeUnit.DAYS);
            }
            //重新生成用户令牌
            userVerificationJwt =  JWTUtil.createJwt(new HashMap<String, Object>() ,JWTUtil.EXPIRE_TIME);
            //将jwt令牌放入 response head中
            response.setHeader(JWTUtil.JWT_VERIFICATION_KEY, userVerificationJwt);
        }
        //刷新缓存,更新验证码
        redisTemplate.opsForValue().set(VERIFICATION_CODE + userVerificationJwt , capText,60, TimeUnit.SECONDS);
        System.out.println(capText);
//        ImageUtil.validateCode(response,captcha,capText);
        //生成图片
        String code = "data:image/png;base64," + ImageUtil.validateCode(captcha,capText);
        return code;
    }

    /**
     * 第二种方式
     * 使用Graphics绘图来定制格式,生成验证码
     * 转成base64编码的字符串输出到前端
     */
    @RequestMapping(value = {"/loginValidateCode1"})
    public String loginValidateCode1(HttpServletRequest request, HttpServletResponse response) throws Exception{
        response.setDateHeader("Expires", 0);
        // Set standard HTTP/1.1 no-cache headers.
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        // Set IE extended HTTP/1.1 no-cache headers (use addHeader).
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        // Set standard HTTP/1.0 no-cache header.
        response.setHeader("Pragma", "no-cache");
        // return a jpeg
        response.setContentType("image/png");

        String str = captcha.createText();//使用DefaultKaptcha规则生成随机码

        //生成图片
        String code = "data:image/png;base64," + ImageUtil.createImageWithVerifyCode(str,200,50);
        return code;
    }

    /**
     * 第三种方式
     * 使用DefaultKaptcha生成登录验证码图片
     * 并用文件流方式输出到前端
     */
    @RequestMapping(value = {"/loginValidateCode2"})
    public void loginValidateCode2(HttpServletRequest request, HttpServletResponse response) throws Exception{
        response.setDateHeader("Expires", 0);
        // Set standard HTTP/1.1 no-cache headers.
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        // Set IE extended HTTP/1.1 no-cache headers (use addHeader).
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        // Set standard HTTP/1.0 no-cache header.
        response.setHeader("Pragma", "no-cache");
        // return a jpeg
        response.setContentType("image/png");

        ImageUtil.validateCode(response,captcha,captcha.createText());

    }

}

ImageUtil公共类

java 复制代码
package com.dream.datacenter.utils;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import java.util.Base64;
import java.util.Base64.Encoder;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Random;

/**
 * Created with IntelliJ IDEA
 *
 * @Author: Mr.HPC
 * @Date: 2022/1/6 23:19
 * @Version 1.0
 * 验证码工具类
 */
public class ImageUtil {

    /**
     * 第一种,用于IO输出
     * @param captchaProducer 生成图片方法类
     * @throws Exception
     */
    public static void validateCode(HttpServletResponse response,
                                      DefaultKaptcha captchaProducer,String capText) throws Exception{
        // create the image with the text
        BufferedImage bi = captchaProducer.createImage(capText);
        ServletOutputStream out = response.getOutputStream();
        // write the data out
        ImageIO.write(bi, "JPEG", out);
        try {
            out.flush();
        } finally {
            out.close();
        }
    }

    /**
     * 第二种
     * 生成验证码图片转 base64,用于方法请求后,将结果赋值到img标签src属性
     * @param captchaProducer 生成图片方法类
     * @throws Exception
     */
    public static String validateCode(DefaultKaptcha captchaProducer,String capText) throws Exception{
        // create the image with the text
        BufferedImage bi = captchaProducer.createImage(capText);
        return returnPicBase64(bi);
    }

    /**
     * 第三种,生成验证码图片转 base64,用于方法请求后,将结果赋值到img标签src属性
     * @param word 要生存的验证码随机字符串
     * @param width 图片宽度
     * @param height 图片高度
     * @return base64 格式生成的验证码图片
     * @throws IOException
     */
    public static String createImageWithVerifyCode(String word, int width, int height) throws IOException {
        String png_base64="";
        //绘制内存中的图片
        BufferedImage bufferedImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        //得到画图对象
        Graphics graphics = bufferedImage.getGraphics();
        //绘制图片前指定一个颜色
        graphics.setColor(getRandColor(160,200));
        graphics.fillRect(0,0,width,height);
        //绘制边框
        graphics.setColor(Color.white);
        graphics.drawRect(0, 0, width - 1, height - 1);
        // 步骤四 四个随机数字
        Graphics2D graphics2d = (Graphics2D) graphics;
        graphics2d.setFont(new Font("宋体", Font.BOLD, 18));
        Random random = new Random();
        // 定义x坐标
        int x = 10;
        for (int i = 0; i < word.length(); i++) {
            // 随机颜色
            graphics2d.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
            // 旋转 -30 --- 30度
            int jiaodu = random.nextInt(60) - 30;
            // 换算弧度
            double theta = jiaodu * Math.PI / 180;
            // 获得字母数字
            char c = word.charAt(i);
            //将c 输出到图片
            graphics2d.rotate(theta, x, 20);
            graphics2d.drawString(String.valueOf(c), x, 20);
            graphics2d.rotate(-theta, x, 20);
            x += 30;
        }
        // 绘制干扰线
        graphics.setColor(getRandColor(160, 200));
        int x1;
        int x2;
        int y1;
        int y2;
        for (int i = 0; i < 30; i++) {
            x1 = random.nextInt(width);
            x2 = random.nextInt(12);
            y1 = random.nextInt(height);
            y2 = random.nextInt(12);
            graphics.drawLine(x1, y1, x1 + x2, x2 + y2);
        }
        graphics.dispose();// 释放资源
        return returnPicBase64(bufferedImage);
    }

    /**
     * 将图片字节数组码转为base64编码
     * @param bi
     * @return
     * @throws IOException
     */
    private static String returnPicBase64(BufferedImage bi) throws IOException {
        String png_base64;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();//io流
        ImageIO.write(bi, "png", baos);//写入流中
        byte[] bytes = baos.toByteArray();//转换成字节
//        BASE64Encoder encoder = new BASE64Encoder();
//        png_base64 = encoder.encodeBuffer(bytes).trim();
        Encoder encoder = Base64.getEncoder();
        png_base64 = encoder.encodeToString(bytes).trim();

        png_base64 = png_base64.replaceAll("\n", "").replaceAll("\r", "");//删除 \r\n
        return png_base64;
    }

    /**设置随机颜色*/
    private static Color getRandColor(int fc, int bc) {
        // 取其随机颜色
        Random random = new Random();
        if (fc > 255) {
            fc = 255;
        }
        if (bc > 255) {
            bc = 255;
        }
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }
}

前端代码的调用

test.js 文件

javascript 复制代码
// 图形校验码接口
export const codeImgUrl = "/api/test/loginValidateCode";

export const codeImgUrl1 = "/api/test/loginValidateCode1";
export const codeImgUrl2 = "/api/test/loginValidateCode2";

yzm.vue 文件,其中 refreshCode2() 方法的 this.ruleForm.codeimg2 = window.URL.createObjectURL(blob); 是将图片io流转成浏览器可以识别的URL

html 复制代码
<template>
    <div class="login-wrap">
      <el-form label-position="left" :model="ruleForm" :rules="rules" ref="ruleForm" label-width="0px" class="demo-ruleForm login-container">
        <el-row>

          <el-col :span="4" class="code-box">
            <el-text>生成验证码图片转 base64,用于方法请求后,将结果赋值到img标签src属性</el-text>
            <img :src="ruleForm.codeimg" alt="验证码" class="codeimg" @click="refreshCode()">
          </el-col>
          <el-col :span="4" class="code-box">
            <el-text>生成验证码图片转 base64,用于方法请求后,将结果赋值到img标签src属性</el-text>
            <img :src="ruleForm.codeimg1" alt="验证码" class="codeimg" @click="refreshCode1()">
          </el-col>
          <el-col :span="4" class="code-box">
            <el-text>生成验证码图片转 base64,用于方法请求后,将结果赋值到img标签src属性</el-text>
            <img :src="ruleForm.codeimg2" alt="验证码" class="codeimg" @click="refreshCode2()">
          </el-col>
        </el-row>
        
      </el-form>
    </div>
  </template>
  
  <script type="text/ecmascript-6">
  import { codeImgUrl,codeImgUrl1,codeImgUrl2 } from '../../api/test';
  import md5 from 'js-md5';
  import axios from 'axios';
  
  
  export default {
    name: 'yzm',
    data() {
      return {
        ruleForm: {
          code: '',
          codeimg: '',
          codeimg1: '',
          codeimg2: ''
        },
        //rules前端验证
        rules: {
          
        }
      }
    },
    // 创建完毕状态(里面是操作)
    created() {
      // 获取图形验证码
      this.refreshCode()
      this.refreshCode1()
      this.refreshCode2()
    },
    methods: {
      
      //获取图形验证码
      refreshCode() {
        // this.ruleForm.codeimg = codeImgUrl+"?t="+Date.now();
        let url = codeImgUrl+"?t="+Date.now();
        axios.get(url).then(res => {
            this.ruleForm.codeimg = res.data
        }).catch(resp => {
            console.log(resp);
        });
      },
      refreshCode1() {
        // this.ruleForm.codeimg = codeImgUrl+"?t="+Date.now();
        let url = codeImgUrl1+"?t="+Date.now();
        axios.get(url).then(res => {
            this.ruleForm.codeimg1 = res.data
        }).catch(resp => {
            console.log(resp);
        });
      },
      refreshCode2() {
        // this.ruleForm.codeimg = codeImgUrl+"?t="+Date.now();
        let url = codeImgUrl2+"?t="+Date.now();
        axios.request({
          url: url,
          responseType: 'blob',
          method: 'get'
        }).then(res => {
          let blob = new Blob([res.data], {
            type: "image/png"
          });
          this.ruleForm.codeimg2 = window.URL.createObjectURL(blob);
        }), (error) => {
          this.$message({
            type: 'warning',
            message: '系统错误,请联系管理员!'
          });
        }
      },
    }
  }
  </script>
  
  <style scoped>
  
  </style>
相关推荐
智慧老师14 分钟前
Spring基础分析13-Spring Security框架
java·后端·spring
lxyzcm16 分钟前
C++23新特性解析:[[assume]]属性
java·c++·spring boot·c++23
V+zmm101341 小时前
基于微信小程序的乡村政务服务系统springboot+论文源码调试讲解
java·微信小程序·小程序·毕业设计·ssm
Oneforlove_twoforjob1 小时前
【Java基础面试题025】什么是Java的Integer缓存池?
java·开发语言·缓存
xmh-sxh-13141 小时前
常用的缓存技术都有哪些
java
迷糊的『迷』2 小时前
vue-axios+springboot实现文件流下载
vue.js·spring boot
AiFlutter2 小时前
Flutter-底部分享弹窗(showModalBottomSheet)
java·前端·flutter
J不A秃V头A2 小时前
IntelliJ IDEA中设置激活的profile
java·intellij-idea
DARLING Zero two♡2 小时前
【优选算法】Pointer-Slice:双指针的算法切片(下)
java·数据结构·c++·算法·leetcode
小池先生2 小时前
springboot启动不了 因一个spring-boot-starter-web底下的tomcat-embed-core依赖丢失
java·spring boot·后端