背景
需要實作一個驗證碼登入的功能需求。這個需求挺簡單的,主要實作驗證碼圖檔生成給前端,然後,在登入接口比對驗證碼即可。剛拿到這個需求,好久沒有搞過登入這一塊了,是以,查了一下相關驗證碼的知識。下面是維基百科中關于驗證碼的說明:
全自動區分計算機和人類的圖靈測試(英語: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("驗證碼驗證失敗");
}
...
}
這裡主要就是從會話取出之前生成的正确驗證碼,與前端送出的驗證碼進行比對。
**注意:**從會話中讀到正确的驗證碼之後,要從會話中删除掉這個資料。
測試
擷取驗證碼接口:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL3UDZkZjZzIGZxAjZhFmY4UWN3QzNzAjN3ImMyEjYwUzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
總結
在使用NanoCaptcha庫之前,還嘗試使用了BotDetect庫,但是BotDetect不支援JDK17就放棄了。而且BotDetect本身自己說Java版本也是測試版本,再加上這邊也隻需要一款簡單夠用的驗證碼就可以了。
參考:
- 驗證碼
- reCAPTCHA
- 《從 reCAPTCHA 遷移到 hCaptcha》
- NanoCaptcha
- SprintBoot returning a PNG from a Controller’s RequestMapping
- BotDetect CAPTCHA Generator