接口信息

生成验证码图片

路径地址 http://localhost:63075/checkcode/pic
请求方式 POST
请求参数 CheckCodeParamsDTO
返回结果 CheckCodeResultVO

校验验证码图片

路径地址 http://localhost:63075/checkcode/verify
请求方式 POST
请求参数 String key, String code
返回结果 Boolean

配置图片生成

使用开源的图片生成工具 Kaptcha,并进行自定义的配置

/**
* 图片验证码配置类
**/
@Configuration
public class KaptchaConfig {

//图片验证码生成器,使用开源的kaptcha
@Bean
public DefaultKaptcha producer() {
Properties properties = new Properties();
properties.put("kaptcha.border", "no");
properties.put("kaptcha.textproducer.font.color", "black");
properties.put("kaptcha.textproducer.char.space", "10");
properties.put("kaptcha.textproducer.char.length", "4");
properties.put("kaptcha.image.height", "34");
properties.put("kaptcha.image.width", "138");
properties.put("kaptcha.textproducer.font.size", "25");

properties.put("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
Config config = new Config(properties);
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}

Model实体类

com.swx.checkcode.model包下创建下面两个实体类:

验证码参数类

CheckCodeParamsDTO
/**
* 验证码生成参数类
*/
@Data
public class CheckCodeParamsDTO {

/**
* 验证码类型:pic、sms、email等
*/
private String checkCodeType;

/**
* 业务携带参数
*/
private String param1;
private String param2;
private String param3;
}

验证码结果类

CheckCodeResultVO
/**
* 验证码结果类
*/
@Data
public class CheckCodeResultVO {

/**
* key用于验证
*/
private String key;

/**
* 混淆后的内容
* 举例:
* 1.图片验证码为:图片base64编码
* 2.短信验证码为:null
* 3.邮件验证码为: null
* 4.邮件链接点击验证为:null
* ...
*/
private String aliasing;
}

Service方法

接口定义

com.swx.checkcode.service包下创建下面接口:

/**
* 验证码接口
*/
public interface CheckCodeService {

/**
* 生成验证码
*
* @param dto 生成验证码参数
* @return com.swx.checkcode.model.CheckCodeResultDTO 验证码结果
*/
CheckCodeResultVO generate(CheckCodeParamsDTO dto);

/**
* 校验验证码
*
* @param key key
* @param code 验证码
* @return boolean 验证结果
*/
public boolean verify(String key, String code);

/**
* 验证码生成器
*/
public interface CheckCodeGenerator {
/**
* 验证码生成
*
* @param length 验证码长度
* @return String 验证码
*/
String generate(int length);
}

/**
* Key生成器
*/
public interface KeyGenerator {
/**
* key生成
*
* @param prefix key前缀
* @return String key
*/
String generate(String prefix);
}

/**
* 验证码存储
*/
public interface CheckCodeStore {

/**
* 向缓存设置key
*
* @param key key
* @param value value
* @param expire 过期时间,单位秒
*/
void set(String key, String value, Integer expire);

String get(String key);

void remove(String key);
}
}

使用抽象类实现该接口,提供基础功能

AbstractCheckCodeService
/**
* 验证码接口
*/
@Slf4j
public abstract class AbstractCheckCodeService implements CheckCodeService {

protected CheckCodeGenerator checkCodeGenerator;
protected KeyGenerator keyGenerator;
protected CheckCodeStore checkCodeStore;

public abstract void setCheckCodeGenerator(CheckCodeGenerator checkCodeGenerator);
public abstract void setKeyGenerator(KeyGenerator keyGenerator);
public abstract void setCheckCodeStore(CheckCodeStore CheckCodeStore);


/**
* 生成验证公用方法
*
* @param dto 生成验证码参数
* @param codeLength 验证码长度
* @param keyPrefix key的前缀
* @param expire 过期时间
* @return com.swx.checkcode.service.AbstractCheckCodeService.GenerateResult 生成结果
*/
public GenerateResult generate(CheckCodeParamsDTO dto, Integer codeLength, String keyPrefix, Integer expire) {
// 生成四位验证码
String code = checkCodeGenerator.generate(codeLength);
// 生成一个key
String key = keyGenerator.generate(keyPrefix);

// 存储验证码
checkCodeStore.set(key, code, expire);
// 返回验证码生成结果
return new GenerateResult(key, code);
}

/**
* 验证方法
*
* @param key key
* @param code 验证码
* @return boolean 验证结果
*/
public boolean verify(String key, String code) {
if (StringUtils.isBlank(key) || StringUtils.isBlank(code)) {
return false;
}
String code_l = checkCodeStore.get(key);
if (code_l == null) {
return false;
}
boolean result = code_l.equalsIgnoreCase(code);
if (result) {
//删除验证码
checkCodeStore.remove(key);
}
return result;
}


@Data
protected static class GenerateResult {
String key;
String code;

GenerateResult() {

}

GenerateResult(String key, String code) {
this.key = key;
this.code = code;
}
}

public abstract CheckCodeResultVO generate(CheckCodeParamsDTO checkCodeParamsDto);
}

实现缓存接口

使用策略模式,实现不同的缓存方案。

内存缓存

将验证码数据存放在内存中

MemoryCheckCodeStore
@Component("MemoryCheckCodeStore")
public class MemoryCheckCodeStore implements CheckCodeService.CheckCodeStore {

Map<String, String> map = new HashMap<String, String>();

/**
* 向缓存设置key
*
* @param key key
* @param value value
* @param expire 过期时间,单位秒
*/
@Override
public void set(String key, String value, Integer expire) {
map.put(key, value);
}

/**
* @param key 值
* @return
*/
@Override
public String get(String key) {
return map.get(key);
}

/**
* @param key 值
*/
@Override
public void remove(String key) {
map.remove(key);
}
}

Redis缓存

使用redis存储验证码,可以设置过期时间

RedisCheckCodeStore
/**
* 使用redis存储验证码
*/
@Component("RedisCheckCodeStore")
public class RedisCheckCodeStore implements CheckCodeService.CheckCodeStore {

private final StringRedisTemplate redisTemplate;

public RedisCheckCodeStore(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}

/**
* 向缓存设置key
*
* @param key key
* @param value value
* @param expire 过期时间,单位秒
*/
@Override
public void set(String key, String value, Integer expire) {
redisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
}

/**
* @param key 键
* @return
*/
@Override
public String get(String key) {
return redisTemplate.opsForValue().get(key);
}

/**
* @param key 键
*/
@Override
public void remove(String key) {
redisTemplate.delete(key);
}
}

实现生成验证码

实现 CheckCodeService.CheckCodeGenerator 接口

NumberLetterCheckCodeGenerator
/**
* 数字字母生成器
*/
@Component("NumberLetterCheckCodeGenerator")
public class NumberLetterCheckCodeGenerator implements CheckCodeService.CheckCodeGenerator {


@Override
public String generate(int length) {
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
int number = random.nextInt(36);
sb.append(str.charAt(number));
}
return sb.toString();
}
}

实现生成Key接口

实现 CheckCodeService.KeyGenerator 接口

UUIDKeyGenerator
/**
* uuid生成器
*/
@Component("UUIDKeyGenerator")
public class UUIDKeyGenerator implements CheckCodeService.KeyGenerator {
@Override
public String generate(String prefix) {
String uuid = UUID.randomUUID().toString();
return prefix + uuid.replaceAll("-", "");
}
}

实现图片验证码

继承 AbstractCheckCodeService 抽象类,实现 CheckCodeService 接口。注入指定的实现类,实现生成图片验证码的同时,将生成的key和code缓存至Redis。

PicCheckCodeServiceImpl
/**
* 图片验证码生成器
*/
@Slf4j
@Service("PicCheckCodeService")
public class PicCheckCodeServiceImpl extends AbstractCheckCodeService implements CheckCodeService {

private final DefaultKaptcha kaptcha;

public PicCheckCodeServiceImpl(DefaultKaptcha kaptcha) {
this.kaptcha = kaptcha;
}

@Resource(name="NumberLetterCheckCodeGenerator")
@Override
public void setCheckCodeGenerator(CheckCodeGenerator checkCodeGenerator) {
this.checkCodeGenerator = checkCodeGenerator;
}

@Resource(name="UUIDKeyGenerator")
@Override
public void setKeyGenerator(KeyGenerator keyGenerator) {
this.keyGenerator = keyGenerator;
}


@Resource(name="RedisCheckCodeStore")
@Override
public void setCheckCodeStore(CheckCodeStore checkCodeStore) {
this.checkCodeStore = checkCodeStore;
}

/**
* 生成验证码
*
* @param dto 生成验证码参数
* @return com.swx.checkcode.model.CheckCodeResultDTO 验证码结果
*/
@Override
public CheckCodeResultVO generate(CheckCodeParamsDTO dto) {
GenerateResult generate = generate(dto, 4, "checkcode:", 60);
String key = generate.getKey();
String code = generate.getCode();
String pic = createPic(code);
CheckCodeResultVO resultDTO = new CheckCodeResultVO();
resultDTO.setAliasing(pic);
resultDTO.setKey(key);
return resultDTO;
}

private String createPic(String code) {
// 生成图片验证码
ByteArrayOutputStream outputStream = null;
BufferedImage image = kaptcha.createImage(code);

outputStream = new ByteArrayOutputStream();
String imgBase64Encoder = null;
try {
// 对字节数组Base64编码
ImageIO.write(image, "png", outputStream);
imgBase64Encoder = "data:image/png;base64," + Base64Utils.encodeToString(outputStream.toByteArray());
} catch (IOException e) {
log.error("图片验证码生成错误", e);
} finally {
try {
outputStream.close();
} catch (IOException e) {
log.error("流关闭失败", e);
}
}
return imgBase64Encoder;
}
}

Controller服务

主要提供验证码获取和验证功能,其中验证功能将由认证授权微服务远程调用,进行登陆时验证码的验证。

CheckCodeController
@Api(value = "验证码服务接口", tags = "验证码服务接口")
@RestController
public class CheckCodeController {

private final CheckCodeService picCheckCodeService;

public CheckCodeController(CheckCodeService picCheckCodeService) {
this.picCheckCodeService = picCheckCodeService;
}

@ApiOperation(value="生成验证信息", notes="生成验证信息")
@PostMapping(value = "/pic")
public CheckCodeResultVO generatePicCheckCode(CheckCodeParamsDTO dto){
return picCheckCodeService.generate(dto);
}

@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){
return picCheckCodeService.verify(key, code);
}
}