图形验证码组件

hutool的验证码已经够用,但有些地方需要自定义(画布超界、去除一些字符),所以对此进行了扩展。

依赖

<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
			<version>4.5.8</version>
		</dependency>

生成器

package com.rz.util;

import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.img.GraphicsUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 自定义验证码生成器(因原生工具经常出现字符超出画布边界,去除一些0和o等字母,特此自定义一个)
 *
 * @author sunziwen
 * @version 1.0
 * @date 2019/9/2 14:24
 **/
public class BuilderCaptcha extends AbstractCaptcha {
    /**
     * 用于随机选的数字
     */
    private static final String BASE_NUMBER = "123456789";
    /**
     * 用于随机选的字符
     */
    private static final String BASE_CHAR_LOWER = "abcdefghjkmnpqrstuvwxyz";
    private static final String BASE_CHAR_UP = "ABCDEFGHJKMNPQRSTUVWXYZ";
    /**
     * 用于随机选的字符和数字
     */
    private static final String BASE_CHAR_NUMBER = BASE_NUMBER + BASE_CHAR_LOWER + BASE_CHAR_UP;

    /**
     * 构造,使用随机验证码生成器生成验证码
     *
     * @param width          图片宽
     * @param height         图片高
     * @param codeCount      字符个数
     * @param interfereCount 验证码干扰元素个数
     */
    public BuilderCaptcha(int width, int height, int codeCount, int interfereCount) {
        super(width, height, new RandomGenerator(BASE_CHAR_NUMBER, codeCount), interfereCount);
    }

    /**
     * 根据生成的code创建验证码图片
     *
     * @param code 验证码
     */
    @Override
    protected Image createImage(String code) {
        // 图像buffer
        final BufferedImage image = new BufferedImage(width + 100, height + 50, BufferedImage.TYPE_INT_RGB);
        final Graphics2D g = GraphicsUtil.createGraphics(image, ObjectUtil.defaultIfNull(this.background, Color.WHITE));

        // 干扰线
        drawInterfere(g);

        // 字符串
        drawString(g, code);

        return image;
    }

    /**
     * 绘制字符串
     *
     * @param g    {@link Graphics2D}画笔
     * @param code 验证码
     */
    private void drawString(Graphics2D g, String code) {
        // 抗锯齿
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // 创建字体
        g.setFont(this.font);

        // 文字
        int minY = GraphicsUtil.getMinY(g);
        if (minY < 0) {
            minY = this.height - 1;
        }

        final int len = this.generator.getLength();
        int charWidth = width / len;
        for (int i = 0; i < len; i++) {
            // 产生随机的颜色值,让输出的每个字符的颜色值都将不同。
            g.setColor(ImgUtil.randomColor());
            g.drawString(String.valueOf(code.charAt(i)), i * charWidth + 50, RandomUtil.randomInt(minY, this.height));
        }
    }

    /**
     * 绘制干扰线
     *
     * @param g {@link Graphics2D}画笔
     */
    private void drawInterfere(Graphics2D g) {
        final ThreadLocalRandom random = RandomUtil.getRandom();
        // 干扰线
        for (int i = 0; i < this.interfereCount; i++) {
            int xs = random.nextInt(width + 200);
            int ys = random.nextInt(height + 100);
            int xe = xs + random.nextInt(width / 8);
            int ye = ys + random.nextInt(height / 8);
            g.setColor(ImgUtil.randomColor(random));
            g.drawLine(xs, ys, xe, ye);
        }
    }
}

附上一个对应的工具类:

package com.rz.util;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.rz.config.Config;
import com.rz.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;

/**
 * 图形验证码工具类
 *
 * @author sunziwen
 * @version 1.0
 * @date 2019/6/10 10:44
 **/
@Component
public class RzCaptchaUtil {
    private RedisUtil redisUtil;
    private Config config;

    @Autowired
    public RzCaptchaUtil(RedisUtil redisUtil, Config config) {
        this.redisUtil = redisUtil;
        this.config = config;
    }

    /**
     * 获取图形验证码
     *
     * @return
     */
    public HashMap<String, Object> getCaptchaBase64() {
        BuilderCaptcha lineCaptcha = new BuilderCaptcha(config.getCaptchWidth(), config.getCaptchHeight(), 4, 150);
        String imageBase64 = lineCaptcha.getImageBase64();
        String code = lineCaptcha.getCode();
        HashMap<String, Object> hashMap = new HashMap<>(2);
        hashMap.put("captcha", "data:image/png;base64," + imageBase64);
        //存储在redis中的key
        String key = IdUtil.randomUUID() + System.currentTimeMillis();
        hashMap.put("verifyStr", key);
        redisUtil.set(key, code, config.getCaptchExpireTime());
        return hashMap;
    }

    /**
     * 校验图形验证码
     *
     * @param code
     * @param verify
     * @return
     */
    public boolean verifyCaptcha(String code, String verify) {
        String redisCode = redisUtil.get(verify) + "";
        if (StrUtil.isNotBlank(code) && StrUtil.equalsIgnoreCase(redisCode, code)) {
            redisUtil.remove(verify);
            return true;
        }
        return false;
    }
}

使用示例:

@PostMapping(value = "/getCaptcha", name = "获取图形验证码")
    @ApiOperation("获取图形验证码")
    public AppResDto getCaptcha() {
        HashMap<String, Object> hashMap = rzCaptchaUtil.getCaptchaBase64();
        return AppResDto.success(hashMap);
    }
@ApiOperation("登录")
    @PostMapping("/login")
    @Limit(program = "rz-xxxx", prefix = "users-login", count = 10, period = 30, limitType = LimitType.ANNOTATION)
    public AppResDto login(@RequestParam("phone") @LimitParam String phone,
                           @RequestParam("password") String password,
                           @RequestParam("captcha") String captcha,
                           @RequestParam("verifyStr") String verifyStr) {
        //图形验证码正确
        AssertUtil.isTrue(ResEnums.verify_error, rzCaptchaUtil.verifyCaptcha(captcha, verifyStr));
        return userController.login(phone, password);
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

文子阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值