SpringBoot实现图片验证码API
2021-05-12 14:41:32
为了提高系统的安全性,防止机器人批量操作,我们常用的方法就是使用图形验证码
## Kaptcha 简介
Kaptcha 出自谷歌验证码工具,可高度配置生成验证码图片:
- 验证码的字体
- 验证码字体的大小
- 验证码字体的字体颜色
- 验证码内容的范围(数字,字母,中文汉字!)
- 验证码图片的大小,边框,边框粗细,边框颜色
- 验证码的干扰线
- 验证码的样式(鱼眼样式、3D、普通模糊、...)
## Kaptcha 详细配置表
| kaptcha.border | 图片边框,合法值:yes , no | yes |
| -------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------- |
| kaptcha.border.color | 边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. | black |
| kaptcha.image.width | 图片宽 | 200 |
| kaptcha.image.height | 图片高 | 50 |
| kaptcha.producer.impl | 图片实现类 | com.google.code.kaptcha.impl.DefaultKaptcha |
| kaptcha.textproducer.impl | 文本实现类 | com.google.code.kaptcha.text.impl.DefaultTextCreator |
| kaptcha.textproducer.char.string | 文本集合,验证码值从此集合中获取 | abcde2345678gfynmnpwx |
| kaptcha.textproducer.char.length | 验证码长度 | 5 |
| kaptcha.textproducer.font.names | 字体 | Arial, Courier |
| kaptcha.textproducer.font.size | 字体大小 | 40px. |
| kaptcha.textproducer.font.color | 字体颜色,合法值: r,g,b 或者 white,black,blue. | black |
| kaptcha.textproducer.char.space | 文字间隔 | 2 |
| kaptcha.noise.impl | 干扰实现类 | com.google.code.kaptcha.impl.DefaultNoise |
| kaptcha.noise.color | 干扰 颜色,合法值: r,g,b 或者 white,black,blue. | black |
| kaptcha.obscurificator.impl | 图片样式:<br />水纹 com.google.code.kaptcha.impl.WaterRipple <br />鱼眼 com.google.code.kaptcha.impl.FishEyeGimpy <br />阴影 com.google.code.kaptcha.impl.ShadowGimpy | com.google.code.kaptcha.impl.WaterRipple |
| kaptcha.background.impl | 背景实现类 | com.google.code.kaptcha.impl.DefaultBackground |
| kaptcha.background.clear.from | 背景颜色渐变,开始颜色 | light grey |
| kaptcha.background.clear.to | 背景颜色渐变, 结束颜色 | white |
| kaptcha.word.impl | 文字渲染器 | com.google.code.kaptcha.text.impl.DefaultWordRenderer |
| kaptcha.session.key | session key | KAPTCHA_SESSION_KEY |
| kaptcha.session.date | session date | KAPTCHA_SESSION_DATE |
## 用法
pom.xml
```xml
<!--https://github.com/penggle/kaptcha-->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sf.ehcache/ehcache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
```
resources/ehcache.xml
```xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!--
以下属性是必须的:
name cache的标识符,在一个CacheManager中必须唯一
maxElementsInMemory 在内存中缓存的element的最大数目
maxElementsOnDisk 在磁盘上缓存的element的最大数目
eternal 设置元素(缓存中对象)是否永久驻留
overflowToDisk 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
以下属性是可选的:
timeToIdleSeconds - 缓存element在过期前的空闲时间。默认为0,表示可空闲无限时间. (如果指定了这个时间,是否在被hit的前超过了这个时间就会被remove,在内存缓存数目超限之前不会被remove)
timeToLiveSeconds - 缓存element的有效生命期。这个类似于timeouts,默认为0,不过期(通常情况下应该大于等于timeToIdleSeconds)
diskPersistent - 在VM重启的时候是否持久化磁盘缓存,默认是false
diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒
memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候,移除缓存中element的策略。默认是LRU,可选的有LFU和FIFO。
-->
<cache name="captcha"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="600"
timeToLiveSeconds="600"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
```
resources/application.properties
```ini
spring.cache.type=ehcache
spring.cache.ehcache.config=classpath:ehcache.xml
```
验证码图片配置
```java
package com.example.demo.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;
@Component
public class KaptchaConfig {
@Bean
public DefaultKaptcha getDefaultKaptcha() {
DefaultKaptcha dk = new DefaultKaptcha();
Properties properties = new Properties();
// 图片边框
properties.setProperty("kaptcha.border", "no");
// 边框颜色
properties.setProperty("kaptcha.border.color", "105,179,90");
// 字体颜色
properties.setProperty("kaptcha.textproducer.font.color", "blue");
// 图片宽
properties.setProperty("kaptcha.image.width", "110");
// 图片高
properties.setProperty("kaptcha.image.height", "40");
// 字体大小
properties.setProperty("kaptcha.textproducer.font.size", "30");
// session key
//properties.setProperty("kaptcha.session.key", "code");
// 验证码长度
//properties.setProperty("kaptcha.textproducer.char.length", "4");
// 字体
//properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
//properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy");
Config config = new Config(properties);
dk.setConfig(config);
return dk;
}
}
```
开启缓存
```java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching // 开启缓存
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
```
服务类
```java
package com.example.demo.service;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
/**
* 图片验证码服务类
*
* @author 邹义良
*/
@Service
public class CaptchaService {
@Autowired
CacheManager cacheManager;
/**
* 验证码工具
*/
@Autowired
DefaultKaptcha defaultKaptcha;
public String create() {
// String random = defaultKaptcha.createText();
String random = getRandomString(4); //验证码图片中的随机字符
String key = getRandomString(32);
Element element = new Element(key, random, 0, 600); //过期时间600秒
cacheManager.getCache("captcha").put(element);
return key;
}
public byte[] getImage(String key, String formatName) throws Exception {
String code = getCode(key);
if (code == null) {
throw new RuntimeException("INVALID_KEY");
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
BufferedImage bi = defaultKaptcha.createImage(code);
ImageIO.write(bi, formatName, out);
byte[] captcha = out.toByteArray();
return captcha;
}
public boolean verify(String key, String code) {
return verify(key, code, true);
}
public boolean verify(String key, String code, boolean ignoreCase) {
String rightCode = getCode(key);
if (rightCode == null) {
return false;
}
if (ignoreCase) {
return rightCode.toLowerCase().equals(code.toLowerCase());
}
return rightCode.equals(code);
}
protected String getCode(String key) {
Element element = cacheManager.getCache("captcha").get(key);
if (element == null) {
return null;
}
return element.getObjectValue().toString();
}
protected static String getRandomString(int stringLength) {
String string = "abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ0123456789";
StringBuffer sb = new StringBuffer();
for (int i = 0; i < stringLength; i++) {
int r = (int) (Math.random() * 10000);
int index = r % string.length();
sb.append(string.charAt(index));
}
return sb.toString();
}
}
```
控制器
```java
package com.example.demo.controller;
import com.example.demo.service.CaptchaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* 图片验证码
*/
@Controller
public class CaptchaController {
@Autowired
CaptchaService captchaService;
@GetMapping("/captcha/create")
@ResponseBody
public Map<String, String> create(HttpServletRequest request, HttpServletResponse response) throws Exception {
String key = captchaService.create();
StringBuffer url = new StringBuffer();
url.append(request.getScheme());
url.append("://");
url.append(request.getServerName());
url.append(":");
url.append(request.getServerPort());
url.append("/captcha/show/");
url.append(key);
Map<String, String> result = new HashMap<String, String>();
result.put("key", key);
result.put("url", url.toString());
return result;
}
@GetMapping("/captcha/show/{key}")
public void show(@PathVariable String key, HttpServletResponse response) throws Exception {
byte[] captcha = captchaService.getImage(key, "png");
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/png");
ServletOutputStream sout = response.getOutputStream();
sout.write(captcha);
sout.flush();
sout.close();
}
}
```