使用Captcha生成验证码, 利用Redis存储验证码
Redis中的结构为, Key是32位的UUID, Value为Captcha的4位随机字母以及数字的集合
设定Redis过期时间为1min, 即可实现过期验证码的自动失效
基本的依赖这里不再叙述, 主要说一下要导入Captcha的依赖
com.github.penggle
kaptcha
2.3.2
所有的依赖如下
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.4.0
com.wang
spring_security_framework
0.0.1-SNAPSHOT
spring_security_framework
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-starter-jdbc
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-validation
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.4
org.thymeleaf.extras
thymeleaf-extras-springsecurity5
mysql
mysql-connector-java
runtime
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.springframework.security
spring-security-test
test
com.alibaba
druid-spring-boot-starter
1.2.2
com.alibaba
fastjson
1.2.74
log4j
log4j
1.2.17
io.springfox
springfox-boot-starter
3.0.0
cn.hutool
hutool-all
5.4.7
com.github.penggle
kaptcha
2.3.2
org.springframework.boot
spring-boot-maven-plugin
配置SpringBoot的配置文件, 这里主要关注一个session的过期时间
#Port
server:
port: 80
servlet:
session:
timeout: 1
spring:
application:
name: SpringSecurityFramework
#dataBase Setting
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#Druid Setting
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 30000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true
#Setting For Druid StatView and Filter
filters: stat,wall,log4j
max-pool-prepared-statement-per-connection-size: 20
use-global-data-source-stat: true
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSql
#Redis Setting
redis:
host: 127.0.0.1
port: 6379
#Thymeleaf
thymeleaf:
cache: false
#Mybatis
mybatis:
type-aliases-package: com.wang.entity
mapper-locations: classpath:Mybatis/mapper/*.xml
configuration:
map-underscore-to-camel-case: true
其余的配置, 如log4j, druid, SpringSecurity, RedisTemplate,这里就不再赘述
我们可以通过JAVA的配置类来配置Captcha生成验证码的一些规则
package com.wang.spring_security_framework.config;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
//Kaptcha配置
@Configuration
public class KaptchaConfig {
@Bean
public DefaultKaptcha producer() {
//Properties类
Properties properties = new Properties();
// 图片边框
properties.setProperty("kaptcha.border", "yes");
// 边框颜色
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.noise.impl","com.google.code.kaptcha.impl.DefaultNoise");
//Kaptcha 使用上述配置
Config config = new Config(properties);
//DefaultKaptcha对象使用上述配置, 并返回这个Bean
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
使用UUID作为key, 同时考虑到对验证码的输出结果可能有不同的要求, 这里写两个工具类来处理它们
package com.wang.spring_security_framework.util;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
public class UUIDUtil {
/**
* 生成32位的随机UUID
* @return 字符形式的小写UUID
*/
@Bean
public String getUUID32() {
return UUID.randomUUID().toString()
.replace("-", "").toLowerCase();
}
}
CaptchaUtil
package com.wang.spring_security_framework.util;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.wang.spring_security_framework.service.CaptchaService;
import io.netty.handler.codec.base64.Base64Encoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import sun.misc.BASE64Encoder;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
@Component
//Captcha 生成工具
public class CaptchaUtil {
@Autowired
private DefaultKaptcha producer;
@Autowired
private CaptchaService captchaService;
//生成catchCreator的map
public Map catchaImgCreator() throws IOException {
//生成文字验证码
String text = producer.createText();
//生成文字对应的图片验证码
BufferedImage image = producer.createImage(text);
//将图片写出
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", outputStream);
//对写出的字节数组进行Base64编码 ==> 用于传递8比特字节码
BASE64Encoder encoder = new BASE64Encoder();
//生成token
Map token = captchaService.createToken(text);
token.put("img", encoder.encode(outputStream.toByteArray()));
return token;
}
}
package com.wang.spring_security_framework.service;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.Map;
public interface CaptchaService {
//生成token
Map createToken(String captcha);
//生成captcha验证码
Map captchaCreator() throws IOException;
//验证输入的验证码是否正确
String versifyCaptcha (String token, String inputCode);
}
2. 实现类
package com.wang.spring_security_framework.service.serviceImpl;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.wang.spring_security_framework.service.CaptchaService;
import com.wang.spring_security_framework.util.CaptchaUtil;
import com.wang.spring_security_framework.util.UUIDUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Service
public class CaptchaServiceImpl implements CaptchaService {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private UUIDUtil uuidUtil;
@Autowired
private CaptchaUtil captchaUtil;
//从SpringBoot的配置文件中取出过期时间
@Value("${server.servlet.session.timeout}")
private Integer timeout;
//UUID为key, 验证码为Value放在Redis中
@Override
public Map createToken(String captcha) {
//生成一个token
String key = uuidUtil.getUUID32();
//生成验证码对应的token 以token为key 验证码为value存在redis中
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set(key, captcha);
//设置验证码过期时间
redisTemplate.expire(key, timeout, TimeUnit.MINUTES);
Map map = new HashMap<>();
map.put("token", key);
map.put("expire", timeout);
return map;
}
//生成captcha验证码
@Override
public Map captchaCreator() throws IOException {
return captchaUtil.catchaImgCreator();
}
//验证输入的验证码是否正确
@Override
public String versifyCaptcha(String token, String inputCode) {
//根据前端传回的token在redis中找对应的value
ValueOperations valueOperations = redisTemplate.opsForValue();
if (redisTemplate.hasKey(token)) {
//验证通过, 删除对应的key
if (valueOperations.get(token).equals(inputCode)) {
redisTemplate.delete(token);
return "true";
} else {
return "false";
}
} else {
return "false";
}
}
}
package com.wang.spring_security_framework.controller;
import com.wang.spring_security_framework.service.CaptchaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.Map;
@RestController
public class LoginController {
@Autowired
CaptchaService captchaService;
@GetMapping("/captcha")
public Map captcha() throws IOException {
return captchaService.captchaCreator();
}
@GetMapping("/login1")
public String login(@RequestParam("token") String token,
@RequestParam("inputCode") String inputCode) {
return captchaService.versifyCaptcha(token, inputCode);
}
}
前端结构如图, 实现了一个简单的验证码
登录
验证通过
完成!