Springboot生成验证码图片

引入依赖包

 com.github.penggle kaptcha 2.3.2

 配置DefaultKaptcha类

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

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 请求方法

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() ,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公共类

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 文件

// 图形校验码接口
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

  

效果