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(); } } ```