背景
需要实现一个验证码登录的功能需求。这个需求挺简单的,主要实现验证码图片生成给前端,然后,在登录接口比对验证码即可。刚拿到这个需求,好久没有搞过登录这一块了,所以,查了一下相关验证码的知识。下面是维基百科中关于验证码的说明:
全自动区分计算机和人类的图灵测试(英语:Completely Automated Public Turing test to tell Computers and Humans Apart,简称CAPTCHA),又称验证码,是一种区分用户是机器或人类的公共全自动程序。在CAPTCHA测试中,作为服务器的计算机会自动生成一个问题由用户来解答。这个问题可以由计算机生成并评判,但是必须只有人类才能解答。由于机器无法解答CAPTCHA的问题,回答出问题的用户即可视为人类。
原来是大名鼎鼎的图灵测试,真高级。现在2022年这个时间点,最出名的验证码是Google的reCAPTCHA,但是在国内是无法集成使用的。面对这个问题,著名CDN cloudflare是换了hCaptcha进行验证。可以看一看,这篇文章《从 reCAPTCHA 迁移到 hCaptcha》。看了看hCaptcha官网,看样子也是要收费的。有没有一款,免费,不用连外网,还支持JDK17的简单验证码库类?那,下面就来尝试一下:NanoCaptcha。
步骤
Maven
<dependency>
<groupId>net.logicsquad</groupId>
<artifactId>nanocaptcha</artifactId>
<version>1.3</version>
</dependency>
生成验证码图片
public BufferedImage getCaptcha(HttpSession session) {
ImageCaptcha imageCaptcha = new ImageCaptcha.Builder(200, 50).addFilter().addBorder().addNoise().addBackground().addContent().build();
// 保存验证码到当前会话
String content = imageCaptcha.getContent();
session.setAttribute(CAPTCHA_KEY, content);
// 返回图片
return imageCaptcha.getImage();
}
这里核心代码就一行:ImageCaptcha imageCaptcha = new ImageCaptcha.Builder(200, 50).addFilter().addBorder().addNoise().addBackground().addContent().build();
使用NanoCaptcha生成验证码图片。
然后,将正确的验证码保存到Spring的集中会话中。最后,返回BufferedImage给请求。Controll方法类似如下:
@GetMapping(value = "/captcha", produces = "image/png")
public BufferedImage getCaptcha(HttpSession session){
return userService.getCaptcha(session);
}
要想Spring正常返回BufferdImage图片,还得注册个Bean,如下:
@Bean
public HttpMessageConverter<BufferedImage> bufferedImageHttpMessageConverter() {
return new BufferedImageHttpMessageConverter();
}
登录验证
@Override
public ResponseEntity<Result> authenticateUser(@Valid @RequestBody LoginRequest loginRequest, HttpSession httpSession) {
// 验证验证码是否正确
String content = (String) httpSession.getAttribute(CAPTCHA_KEY);
httpSession.removeAttribute(CAPTCHA_KEY);
if (!(StringUtils.hasText(content) && content.equals(loginRequest.getCaptcha()))) {
throw new MyException("验证码验证失败");
}
...
}
这里主要就是从会话取出之前生成的正确验证码,与前端提交的验证码进行比对。
**注意:**从会话中读到正确的验证码之后,要从会话中删除掉这个数据。
测试
获取验证码接口:
总结
在使用NanoCaptcha库之前,还尝试使用了BotDetect库,但是BotDetect不支持JDK17就放弃了。而且BotDetect本身自己说Java版本也是测试版本,再加上这边也只需要一款简单够用的验证码就可以了。
参考:
- 验证码
- reCAPTCHA
- 《从 reCAPTCHA 迁移到 hCaptcha》
- NanoCaptcha
- SprintBoot returning a PNG from a Controller’s RequestMapping
- BotDetect CAPTCHA Generator