Skip to content

验证码生成工具

easy-captcha

easy-captcha是在github上找的开源项目。

因工作中有负责做登录的模块,并且在工期较短的情况下使用了该项目同时也避免了重复造轮子浪费时间的情况。

使用该项目也是对比了google的开源验证码项目Kaptcha,由于Google的这个项目比较旧显示出来的效果不太符合项目所以弃用了。

easy-captcha开始

引入依赖

在maven的pom文件中引入依赖,由于是该项目在GitHub中属于个人的一个开源项目,最好是将源码下载下来打包后放在自己私有仓库中,或者将源码魔改引入项目中,避免后续项目关闭带来损失。

xml
<dependency>
    <groupId>com.github.whvcse</groupId>
    <artifactId>easy-captcha</artifactId>
    <version>1.6.2</version>
</dependency>

自定义生成器类

该开源项目是提供了一个生成器类,但是并不适用分布式项目。

原因:该项目使用的是基于session存储验证码的方式,不同请求可能会打在不同服务器上,导致会话无效。

新增一个类继承原有SpecCaptcha

java
import com.wf.captcha.SpecCaptcha;

/**
 * @ClassName CaptchaGenerator
 * @Author liupengxiang
 * @Date 2023/7/3 14:57
 * @Description 验证码生成
 */
public class CaptchaGenerator extends SpecCaptcha {

    private CaptchaGenerator() {
        super();
    }
    private CaptchaGenerator(int width,int height) {
        super();
        setWidth(width);
        setHeight(height);
    }
    private CaptchaGenerator(int width,int height,String code) {
        this(width,height);
        setCode(code);
    }
    private CaptchaGenerator(String code) {
        super();
        setCode(code);
    }

    public static CaptchaGenerator generator(int width,int height) {
        return new CaptchaGenerator(width,height);
    }
    public static CaptchaGenerator generator(int width,int height,String code) {
        return new CaptchaGenerator(width,height,code);
    }
    public static CaptchaGenerator generator(String code) {
        return new CaptchaGenerator(code);
    }

    public static CaptchaGenerator generator() {
        return new CaptchaGenerator();
    }

    public void setCode(String code) {
        super.chars = code;
    }
    public String getCode(){
        return super.text();
    }
}

引入redis

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

使用

生成验证码ID

java
    @Override
    public ApiResult<JSONObject> generateVerifyId() {
        String verifyId = IdUtil.fastSimpleUUID();
        CaptchaGenerator generator = CaptchaGenerator.generator();
        generator.setLen(4);
        String capStr = generator.getCode();
        log.info("验证码:{}",capStr);
        stringRedisTemplate.opsForValue().set(RedisPrefix.VERIFY_CODE_WITH_ID + verifyId, capStr,60, TimeUnit.SECONDS);
        JSONObject payload = new JSONObject();
        payload.put("verifyId",verifyId);
        return ApiResult.build(payload);
    }

使用验证码ID获取验证码图片

java
    @Override
    public void getVerifyCode(String verifyId, HttpServletResponse response) {
        String capStr = stringRedisTemplate.opsForValue().get(RedisPrefix.VERIFY_CODE_WITH_ID + verifyId);
        log.info(capStr);
        if (StringUtils.isBlank(capStr)) {
            try {
                response.sendError(ResultStatus.VERIFY_OVERDUE.getStatus(),ResultStatus.VERIFY_OVERDUE.getMsg());
                return;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        ServletOutputStream out = null;
        try {
            CaptchaGenerator generator = CaptchaGenerator.generator(200,50);
            generator.setCode(capStr);
            out = response.getOutputStream();
            generator.out(out);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

验证

登录逻辑大相径庭这里就只展示验证码校验的代码

java
    String username = loginVO.getUsername();
    String verifyCode = loginVO.getVerifyCode();
    String verifyId = loginVO.getVerifyId();
    String capStr = stringRedisTemplate.opsForValue().get(RedisPrefix.VERIFY_CODE_WITH_ID + verifyId);
    if (StringUtils.isBlank(capStr)) {
        return ApiResult.build(ResultStatus.VERIFY_OVERDUE.getStatus(),ResultStatus.VERIFY_OVERDUE.getMsg());
    }
    log.info("输入验证码信息:{},校验验证码信息:{}",verifyCode,capStr);
    if (!capStr.equalsIgnoreCase(verifyCode)) {
        log.info("验证码错误");
        return ApiResult.build(ResultStatus.VERIFY_ERROR.getStatus(),ResultStatus.VERIFY_ERROR.getMsg());
    }
    log.info("验证码校验成功");

说明LoginVO是封装的接收参数的类,

java
import com.lh.aop.CPGroup;
import lombok.Data;

import javax.validation.constraints.NotBlank;

/**
 * @ClassName SystemUserDTO
 * @Author liupengxiang
 * @Date 2023/6/14 17:28
 * @Description 登录用户信息
 */
@Data
public class LoginVO {
    @NotBlank(groups = {CPGroup.First.class},message = "用户名不能为空")
    private String username;

    @NotBlank(groups = {CPGroup.Second.class},message = "密码不能为空")
    private String password;

    @NotBlank(groups = {CPGroup.Third.class},message = "验证码不能为空")
    private String verifyCode;

    @NotBlank(groups = {CPGroup.Third.class},message = "验证id不能为空")
    private String verifyId;
}

CPGroup

CPGroup是自定义的校验参数顺序的接口,配合@Validated和统一异常处理完成,统一异常处理可参照 参数校验器

java
import javax.validation.GroupSequence;

/**
 * @ClassName CPGroup
 * @Author liupengxiang
 * @Date 2023/6/19 18:33
 * @Description 组序
 */
@GroupSequence({CPGroup.First.class, CPGroup.Second.class, CPGroup.Third.class})
public interface CPGroup {

    interface First {
    }

    interface Second {
    }

    interface Third {

    }

}

CPGroup使用

@Validated -> import org.springframework.validation.annotation.Validated;

java
    @PostMapping("login")
    @ResponseBody
    @ApiOperation("登录")
    public ApiResult<?> login(HttpServletRequest req, @RequestBody @Validated(CPGroup.class) LoginVO loginVO){
        return authService.login(req, loginVO);
    }