0. 简介
Kaptcha是谷歌开源的简单实用的验证码生成工具。通过设置参数,可以自定义验证码大小、颜色、显示的字符等等。
这篇博客基于SpringBoot + Kaptcha + Redis实现一个简单的图片验证码功能
1. 添加Maven依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <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-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.11.1</version> </dependency>
|
2. 添加配置类
2.1 Kaptcha配置
注册 DefaultKaptcha
到 IOC 容器中,在 config 包中,新建 KaptchaConfig
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Configuration public class KaptchaConfig {
@Bean public DefaultKaptcha getDefaultKaptcha(){ DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); Properties properties = new Properties(); properties.put(Constants.KAPTCHA_BORDER, "no"); properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_STRING, "0123456789"); properties.setProperty(Constants.KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); Config config = new Config(properties); defaultKaptcha.setConfig(config); return defaultKaptcha; } }
|
可供配置的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| KAPTCHA_SESSION_KEY = "KAPTCHA_SESSION_KEY"; KAPTCHA_SESSION_DATE = "KAPTCHA_SESSION_DATE"; KAPTCHA_SESSION_CONFIG_KEY = "kaptcha.session.key"; KAPTCHA_SESSION_CONFIG_DATE = "kaptcha.session.date"; KAPTCHA_BORDER = "kaptcha.border"; KAPTCHA_BORDER_COLOR = "kaptcha.border.color"; KAPTCHA_BORDER_THICKNESS = "kaptcha.border.thickness"; KAPTCHA_NOISE_COLOR = "kaptcha.noise.color"; KAPTCHA_NOISE_IMPL = "kaptcha.noise.impl"; KAPTCHA_OBSCURIFICATOR_IMPL = "kaptcha.obscurificator.impl"; KAPTCHA_PRODUCER_IMPL = "kaptcha.producer.impl"; KAPTCHA_TEXTPRODUCER_IMPL = "kaptcha.textproducer.impl"; KAPTCHA_TEXTPRODUCER_CHAR_STRING = "kaptcha.textproducer.char.string"; KAPTCHA_TEXTPRODUCER_CHAR_LENGTH = "kaptcha.textproducer.char.length"; KAPTCHA_TEXTPRODUCER_FONT_NAMES = "kaptcha.textproducer.font.names"; KAPTCHA_TEXTPRODUCER_FONT_COLOR = "kaptcha.textproducer.font.color"; KAPTCHA_TEXTPRODUCER_FONT_SIZE = "kaptcha.textproducer.font.size"; KAPTCHA_TEXTPRODUCER_CHAR_SPACE = "kaptcha.textproducer.char.space"; KAPTCHA_WORDRENDERER_IMPL = "kaptcha.word.impl"; KAPTCHA_BACKGROUND_IMPL = "kaptcha.background.impl"; KAPTCHA_BACKGROUND_CLR_FROM = "kaptcha.background.clear.from"; KAPTCHA_BACKGROUND_CLR_TO = "kaptcha.background.clear.to"; KAPTCHA_IMAGE_WIDTH = "kaptcha.image.width"; KAPTCHA_IMAGE_HEIGHT = "kaptcha.image.height";
|
2.2 Redis配置
application.yml中的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| spring: redis: host: ***.***.***.*** port: 6379 password: ****** database: 0 timeout: 180000 lettuce: pool: max-active: 20 max-wait: -1 max-idle: 5 min-idle: 0
|
注册 RedisTemplate
到 IOC 容器中,在 config 包中,新建 RedisConfig
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @EnableCaching @Configuration public class RedisConfig extends CachingConfigurerSupport {
@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); GenericToStringSerializer genericToStringSerializer = new GenericToStringSerializer(Object.class); template.setValueSerializer(genericToStringSerializer); template.setKeySerializer(new StringRedisSerializer()); template.afterPropertiesSet(); return template; }
@Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(600)) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); return cacheManager; } }
|
3. 接口开发
3.1 Controller层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @Autowired KaptchaService kaptchaService;
@GetMapping("/captcha/send") public void getCaptcha(HttpServletRequest request, HttpServletResponse response) { String deviceCode = request.getHeader("deviceCode"); BufferedImage image = kaptchaService.getCaptcha(deviceCode); ServletOutputStream outputStream; try { outputStream = response.getOutputStream(); ImageIO.write(image, "jpg", outputStream); outputStream.flush(); outputStream.close(); } catch (IOException e) { e.printStackTrace(); } }
@GetMapping("/captcha/check") public void checkCaptcha(@RequestParam("captcha") String captcha, HttpServletRequest request) { String deviceCode = request.getHeader("deviceCode"); boolean result = kaptchaService.checkCaptcha(captcha, deviceCode); }
|
3.2 Service层
设置有效期为30分钟,且30min内最多尝试20次,根据设备id作为唯一标识
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| @Autowired RedisTemplate<String, Object> redisTemplate; @Autowired Producer producer;
@Override public BufferedImage getCaptcha(String deviceCode) { String captchaText = producer.createText(); String key = deviceCode + ":captcha"; String countKey = "count:" + deviceCode + ":captcha"; String count = (String) redisTemplate.opsForValue().get(countKey); if (count == null) { redisTemplate.opsForValue().set(countKey, "1", 30, TimeUnit.MINUTES); } else if (Integer.parseInt(count) <= 20) { redisTemplate.opsForValue().increment(countKey); } else if (Integer.parseInt(count) > 20) { throw new RuntimeException("请稍后尝试"); } redisTemplate.opsForValue().set(key, captchaText, 30, TimeUnit.MINUTES);
return producer.createImage(captchaText); }
@Override public boolean checkCaptcha(String captcha, String deviceCode) { String key = "captcha" + deviceCode + ":code"; String value = (String) redisTemplate.opsForValue().get(key); if (captcha.equals(value)) { redisTemplate.delete(key); return true; } else { return false; } }
|
4. 测试
使用Apifox进行测试