生成图片验证码
为了保护系统的安全,在进行一些比较重要的操作时都需要输入验证码,因为验证码可以防止恶性攻击如XSS跨站脚本攻击
和CSRF跨站请求伪造攻击
应用场景
: 认证, 找回密码, 人机判断, 支付验证等验证码类型
: 图片、语音、手机短信验证码等
环境搭建
第一步: 在工程根目录下创建验证码服务工程xuecheng-plus-checkcode
为其他微服务的各种业务提供验证码的生成、校验
等服务
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--common-pool-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--kaptcha-->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
第二步: 由于验证码是缓存在redis中的,所以我们需要部署Redis
docker pull redis
docker run -d --name myredis -p 6379:6379 redis
docker start myredis
第三步: 在Nacos的dev环境下新增checkcode-dev.yaml
和redis-dev.yaml(group设置为xuecheng-plus-common)
# checkcode-dev.yaml
server:
servlet:
context-path: /checkcode
port: 63075
# redis-dev.yaml
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0
lettuce:
pool:
max-active: 20
max-idle: 10
min-idle: 0
timeout: 10000
#redisson:
#配置文件目录
#config: classpath:singleServerConfig.yaml
第四步: 在本地配置bootstrap.yml
文件
spring:
application:
name: checkcode
cloud:
nacos:
server-addr: 192.168.101.65:8848
discovery:
namespace: dev
group: xuecheng-plus-project
config:
namespace: dev
group: xuecheng-plus-project
file-extension: yaml
refresh-enabled: true
shared-configs:
- data-id: swagger-${spring.profiles.active}.yaml
group: xuecheng-plus-common
refresh: true
- data-id: logging-${spring.profiles.active}.yaml
group: xuecheng-plus-common
refresh: true
- data-id: redis-${spring.profiles.active}.yaml # 引入redis的配置
group: xuecheng-plus-common
refresh: true
profiles:
active: dev
第五步: 在网关工程的的gateway-dev.yaml
配置文件新增网关路由到认证服务和验证码服务
的配置
spring:
cloud:
gateway:
routes: # 网关路由配置
- id: auth-service # 路由的目标地址
uri: lb://auth-service
predicates:
- Path=/auth/**
- id: checkcode # 路由的目标地址
uri: lb://checkcode
predicates:
- Path=/checkcode/**
请求响应模型类
请求模型类
@Data
public class CheckCodeParamsDto {
/**
* 验证码类型:pic、sms、email等
*/
private String checkCodeType;
/**
* 业务携带参数
*/
private String param1;
private String param2;
private String param3;
}
响应模型类
@Data
public class CheckCodeResultDto {
// 存储验证码的key
private String key;
// 对验证码进行脱敏,图片验证码为base64编码(加密),短信验证码为:null,邮件验证码为: null,邮件链接点击验证为:null
private String aliasing;
}
生成图片验证码
第一步: 定义api接口
并接受请求参数
@Api(value = "验证码服务接口")
@RestController
public class CheckCodeController {
@Resource(name = "PicCheckCodeService")
private CheckCodeService picCheckCodeService;
@ApiOperation(value = "生成验证信息", notes = "生成验证信息")
@PostMapping(value = "/pic")
public CheckCodeResultDto generatePicCheckCode(CheckCodeParamsDto checkCodeParamsDto) {
return picCheckCodeService.generate(checkCodeParamsDto);
}
}
第二步:定义service接口CheckCodeService
,定义生成和校验验证码的方法,并定义验证码生成器子接口,key生成器子接口,验证码存储器子接口
- 设计子接口允许开发者根据需要实现不同的
验证码生成策略、key生成策略和验证码存储策略
,从而实现高度的模块化和可扩展性
public interface CheckCodeService {
/**
* @param checkCodeParamsDto 生成验证码参数
* @return com.xuecheng.checkcode.model.CheckCodeResultDto 验证码结果
* @description 生成验证码
*/
CheckCodeResultDto generate(CheckCodeParamsDto checkCodeParamsDto);
/**
* @description 验证码生成器
*/
public interface CheckCodeGenerator {
// 验证码
String generate(int length);
}
/**
* @description key生成器
*/
public interface KeyGenerator {
// key生成
String generate(String prefix);
}
/**
* @description 验证码存储器
*/
public interface CheckCodeStore {
/**
* @param key key
* @param value value
* @param expire 过期时间,单位秒
* @description 向缓存设置key
*/
void set(String key, String value, Integer expire);
String get(String key);
void remove(String key);
}
}
第三步: 定义CheckCodeGenerator
验证码生成器的实现类
@Component("NumberLetterCheckCodeGenerator")
public class NumberLetterCheckCodeGenerator implements CheckCodeService.CheckCodeGenerator {
@Override
public String generate(int length) {
String str="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random=new Random();
StringBuffer sb=new StringBuffer();
for(int i=0;i<length;i++){
// 生成一个范围在0到35之间的随机数,因为str的长度是36
int number=random.nextInt(36);
// 使用这个随机数作为索引从str中取出一个字符,并追加到StringBuffer中
sb.append(str.charAt(number));
}
return sb.toString();
}
}
第四步: 定义KeyGenerator
key生成器的实现类
@Component("UUIDKeyGenerator")
public class UUIDKeyGenerator implements CheckCodeService.KeyGenerator {
@Override
public String generate(String prefix) {
String uuid = UUID.randomUUID().toString();
return prefix + uuid.replaceAll("-", "");
}
}
第五步: 定义CheckCodeStore
验证码存储器的实现类,这里是将生成的验证码存储到Redis当中,验证码对应生成的key
需要返回给前端,当用户提交验证码的时候需要使用
@Component("MemoryCheckCodeStore")
public class MemoryCheckCodeStore implements CheckCodeService.CheckCodeStore {
// 注入StringRedisTemplate
@Autowired
StringRedisTemplate redisTemplate;
@Override
public void set(String key, String value, Integer expire) {
redisTemplate.opsForValue().set(key, value, expire, TimeUnit.MINUTE);
}
@Override
public String get(String key) {
return (String) redisTemplate.opsForValue().get(key);
}
@Override
public void remove(String key) {
redisTemplate.delete(key);
}
}
// 使用本地内存存储验证码
Map<String,String> map = new HashMap<String,String>();
@Override
public void set(String key, String value, Integer expire) {
map.put(key,value);
}
@Override
public String get(String key) {
return map.get(key);
}
@Override
public void remove(String key) {
map.remove(key);
}
第三步: 定义AbstractCheckCodeService(适配器)
实现CheckCodeService
的部分方法,定义内部类GenerateResult
存储生成的key和code(验证码)
@Slf4j
public abstract class AbstractCheckCodeService implements CheckCodeService {
// 验证码生成器
protected CheckCodeGenerator checkCodeGenerator;
// key生成器
protected KeyGenerator keyGenerator;
// 验证码存储器
protected CheckCodeStore checkCodeStore;
// 存储key和code
@Data
protected class GenerateResult{
String key;
String code;
}
/**
* @description 生成验证码的公用方法
* @param checkCodeParamsDto 生成验证码参数
* @param code_length 验证码长度
* @param keyPrefix key的前缀
* @param expire 过期时间
* @return GenerateResult 生成结果(验证码和对应存储key)
*/
public GenerateResult generate(CheckCodeParamsDto checkCodeParamsDto,Integer code_length,String keyPrefix,Integer expire){
// 生成四位验证码
String code = checkCodeGenerator.generate(code_length);
log.debug("生成验证码:{}",code);
// 生成验证码在Redis中存储的key
String key = keyGenerator.generate(keyPrefix);
// 将生成的验证码存储到Redis当中
checkCodeStore.set(key,code,expire);
// 返回验证码生成结果
GenerateResult generateResult = new GenerateResult();
generateResult.setKey(key);
generateResult.setCode(code);
return generateResult;
}
// 等待子类实现核心方法
public abstract void setCheckCodeGenerator(CheckCodeGenerator checkCodeGenerator);
public abstract void setKeyGenerator(KeyGenerator keyGenerator);
public abstract void setCheckCodeStore(CheckCodeStore CheckCodeStore);
// 生成图片验证码
public abstract CheckCodeResultDto generate(CheckCodeParamsDto checkCodeParamsDto);
}
定义service接口实现类并继承AbstractCheckCodeService
,注入CheckCodeGenerator,KeyGenerator,CheckCodeStore
接口的实现类并实现生成图片验证码的业务逻辑
@Service("PicCheckCodeService")
public class PicCheckCodeServiceImpl extends AbstractCheckCodeService implements CheckCodeService {
// 用于生成Kaptcha验证码的组件
@Autowired
private DefaultKaptcha kaptcha;
// 注入CheckCodeGenerator接口的实现类
@Resource(name="NumberLetterCheckCodeGenerator")
@Override
public void setCheckCodeGenerator(CheckCodeGenerator checkCodeGenerator) {
this.checkCodeGenerator = checkCodeGenerator;
}
// 注入KeyGenerator接口的实现类
@Resource(name="UUIDKeyGenerator")
@Override
public void setKeyGenerator(KeyGenerator keyGenerator) {
this.keyGenerator = keyGenerator;
}
// 注入CheckCodeStore接口的实现类
@Resource(name="MemoryCheckCodeStore")
@Override
public void setCheckCodeStore(CheckCodeStore checkCodeStore) {
this.checkCodeStore = checkCodeStore;
}
// 生成图片验证码
@Override
public CheckCodeResultDto generate(CheckCodeParamsDto checkCodeParamsDto) {
// 调用公用的方法生成四位验证码,将验证码缓存到Redis当中并指定key的前缀和有效期
GenerateResult generate = generate(checkCodeParamsDto, 4, "checkcode:", 60);
// GenerateResult中存储了生成的验证码的key和值
String key = generate.getKey();
String code = generate.getCode();
// 将生成的验证码使用Base64编码转换为图片(可以在浏览器中直接打开)
String pic = createPic(code);
// 将图片的Base64编码存储在CheckCodeResultDto对象中返回
CheckCodeResultDto checkCodeResultDto = new CheckCodeResultDto();
checkCodeResultDto.setAliasing(pic);
checkCodeResultDto.setKey(key);
return checkCodeResultDto;
}
// 根据验证码code生成对应的图片
private String createPic(String code) {
ByteArrayOutputStream outputStream = null;
// 使用kaptcha对象生成一个包含验证码值的图片
BufferedImage image = kaptcha.createImage(code);
outputStream = new ByteArrayOutputStream();
String imgBase64Encoder = null;
try {
// 将图片转换为字节数组,并对字节数组进行Base64编码
BASE64Encoder base64Encoder = new BASE64Encoder();
ImageIO.write(image, "png", outputStream);
// 返回一个以"data:image/png;base64,"开头的Base64编码的字符串,这个字符串可以直接在HTML中作为图片的源使用,即浏览器可以直接访问
imgBase64Encoder = "data:image/png;base64," + EncryptUtil.encodeBase64(outputStream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return imgBase64Encoder;
}
}
在base
工程下定义一个工具类:处理网络请求和响应、数据存储以及与外部系统交互时不同格式之间的数据转换
public class EncryptUtil {
private static final Logger logger = LoggerFactory.getLogger(EncryptUtil.class);
// 将字节数组转换为Base64编码的字符串
public static String encodeBase64(byte[] bytes){
String encoded = Base64.getEncoder().encodeToString(bytes);
return encoded;
}
// 将Base64编码的字符串解码为字节数组
public static byte[] decodeBase64(String str){
byte[] bytes = null;
bytes = Base64.getDecoder().decode(str);
return bytes;
}
// 将UTF-8编码的字符串转换为Base64编码的字符串
public static String encodeUTF8StringBase64(String str){
String encoded = null;
try {
encoded = Base64.getEncoder().encodeToString(str.getBytes("utf-8"));
} catch (UnsupportedEncodingException e) {
logger.warn("不支持的编码格式",e);
}
return encoded;
}
// 将Base64编码的字符串解码为UTF-8编码的字符串
public static String decodeUTF8StringBase64(String str){
String decoded = null;
byte[] bytes = Base64.getDecoder().decode(str);
try {
decoded = new String(bytes,"utf-8");
}catch(UnsupportedEncodingException e){
logger.warn("不支持的编码格式",e);
}
return decoded;
}
// 对URL进行编码,以便在URL中安全地传输特殊字符
public static String encodeURL(String url) {
String encoded = null;
try {
encoded = URLEncoder.encode(url, "utf-8");
} catch (UnsupportedEncodingException e) {
logger.warn("URLEncode失败", e);
}
return encoded;
}
// 对编码后的URL进行解码,以还原原始URL
public static String decodeURL(String url) {
String decoded = null;
try {
decoded = URLDecoder.decode(url, "utf-8");
} catch (UnsupportedEncodingException e) {
logger.warn("URLDecode失败", e);
}
return decoded;
}
// 测试
public static void main(String [] args){
String str = "abcd{'a':'b'}";
String encoded = EncryptUtil.encodeUTF8StringBase64(str);
String decoded = EncryptUtil.decodeUTF8StringBase64(encoded);
System.out.println(str);
System.out.println(encoded);
System.out.println(decoded);
String url = "== wo";
String urlEncoded = EncryptUtil.encodeURL(url);
String urlDecoded = EncryptUtil.decodeURL(urlEncoded);
System.out.println(url);
System.out.println(urlEncoded);
System.out.println(urlDecoded);
}
}
测试生成验证码
第一步: 使用HttpClient访问CheckCodeController
中生成验证码图片的接口,响应的图片以base64编码
格式存储,同时在Redis中也可以看到我们缓存的验证码
// 获取验证码图片
POST localhost:63075/checkcode/pic
{
// 生成的验证码存储在Redis中对应的key
"key": "checkcode:20a2ccb511bc472ea785db14d0a547ba",
/*响应的图片是以base64编码格式存储的,我们可以直接在浏览器中访问*/ "aliasing":"data:image/png;base64,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"
}
校验验证码
校验用户提交的验证码
第一步:定义接口
@Api(value = "验证码服务接口")
@RestController
public class CheckCodeController {
@Resource(name = "PicCheckCodeService")
private CheckCodeService picCheckCodeService;
@ApiOperation(value = "校验", notes = "校验")
@ApiImplicitParams({
@ApiImplicitParam(name = "name", value = "业务名称", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "key", value = "验证key", required = true, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "code", value = "验证码", required = true, dataType = "String", paramType = "query")
})
@PostMapping(value = "/verify")
public Boolean verify(String key, String code) {
Boolean isSuccess = picCheckCodeService.verify(key, code);
return isSuccess;
}
}
public interface CheckCodeService {
/**
* @param key
* @param code
* @description 校验验证码
*/
public boolean verify(String key, String code);
}
第二步: 在AbstractCheckCodeService
抽象类中实现校验验证码的业务逻辑
@Slf4j
public abstract class AbstractCheckCodeService implements CheckCodeService {
/**
* 校验验证码
* @param key 提交的验证码key
* @param code 提交的验证码
* @return
*/
public boolean verify(String key, String code){
if (StringUtils.isBlank(key) || StringUtils.isBlank(code)){
return false;
}
// 根据key从Redis缓存中取出正确的验证码和用户输入的验证码进行比对,如果相同则校验通过,否则不通过
String code_l = checkCodeStore.get(key);
if (code_l == null){
return false;
}
// 比较缓存的code_l和传入的code是否相等
boolean result = code_l.equalsIgnoreCase(code);
if(result){
// 删除缓存的验证码
checkCodeStore.remove(key);
}
return result;
}
}
测试校验验证码
使用HttpClient访问CheckCodeController
中校验验证码的接口,请求时携带生成验证码时返回的key
和图片中的验证码
POST localhost:63075/checkcode/verisfy?key=checkcode:c3dce1413f95414e943dcf0a97983fe8&code=ZEUY