|
一、背景
在实现登录功能时,为了防止特定的程序暴力破解,一般为了安全都会在用户登录时增加otp动态验证码录。otp验证码 otp全称叫One-time Password,也称动态口令,是指计算机系统或其他数字设备上只能使用一次的密码,有效期为只有一次登录会话或很短。
常见验证码分为图片验证码和短信验证码,还有滑动窗口模块和选中指定物体验证方式。下面通过Java来实现图片验证码示例,效果如下:

二、实现步骤
1、maven中加入依赖
pom.xml引入依赖:
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>1.6.2</version>
</dependency>2、CaptchaController.java
/**
* 验证码
*/
@GetMapping(&#34;/captcha/digit&#34;)
@ApiOperation(value = &#34;获取数字验证码&#34;, notes = &#34;获取数字验证码&#34;, tags = &#34;验证码相关&#34;)
@ApiImplicitParams({
@ApiImplicitParam(name = &#34;uuid&#34;, value = &#34;uuid&#34;, required = true, paramType = &#34;query&#34;)
})
@PassToken
public void captcha(HttpServletResponse response, String uuid) throws IOException {
response.setHeader(&#34;Cache-Control&#34;, &#34;no-store, no-cache&#34;);
response.setContentType(&#34;image/jpeg&#34;);
log.info(&#34;获取验证码,uuid:{}&#34;, uuid);
//获取图片验证码
BufferedImage image = captchaService.getCaptcha(uuid);
log.info(&#34;获取验证码,uuid:{},return:{}&#34;, uuid, JSON.toJSONString(image));
ServletOutputStream out = response.getOutputStream();
ImageIO.write(image, &#34;jpg&#34;, out);
IOUtils.closeQuietly(out);
}
@GetMapping(&#34;/captcha/graphics&#34;)
@ApiOperation(value = &#34;获取图形验证码&#34;, notes = &#34;获取图形验证码&#34;, tags = &#34;验证码相关&#34;)
@ApiImplicitParams({
@ApiImplicitParam(name = &#34;uuid&#34;, value = &#34;uuid&#34;, required = true, paramType = &#34;query&#34;),
@ApiImplicitParam(name = &#34;type&#34;, value = &#34;类型 png:png gif:gif cn:中文 cngif:中文gif arithmeti:算术&#34;, required = false, paramType = &#34;query&#34;)
})
public void captcha(HttpServletRequest request, HttpServletResponse response,
@RequestParam String uuid,
@RequestParam(defaultValue = &#34;arithmeti&#34;, required = false) String type) throws Exception {
// 设置请求头为输出图片类型
response.setContentType(&#34;image/gif&#34;);
response.setHeader(&#34;Pragma&#34;, &#34;No-cache&#34;);
response.setHeader(&#34;Cache-Control&#34;, &#34;no-cache&#34;);
response.setDateHeader(&#34;Expires&#34;, 0);
Captcha captcha = null;
switch (type) {
case &#34;png&#34;:
captcha = new SpecCaptcha(130, 48);
break;
case &#34;gif&#34;:
// gif类型
captcha = new GifCaptcha(130, 48);
break;
case &#34;cn&#34;:
// 中文类型
captcha = new ChineseCaptcha(130, 48, 5, new Font(&#34;楷体&#34;, Font.PLAIN, 28));
break;
case &#34;cngif&#34;:
// 中文gif类型
captcha = new ChineseGifCaptcha(130, 48, 5, new Font(&#34;楷体&#34;, Font.PLAIN, 28));
break;
case &#34;arithmeti&#34;:
// 算术类型
ArithmeticCaptcha arithmeticCaptcha = new ArithmeticCaptcha(130, 48);
arithmeticCaptcha.setLen(3); // 几位数运算,默认是两位
arithmeticCaptcha.getArithmeticString(); // 获取运算的公式:3+2=?
arithmeticCaptcha.text(); // 获取运算的结果:5
captcha = arithmeticCaptcha;
break;
default:
new SpecCaptcha(130, 48);
break;
}
log.info(&#34;验证码:{}&#34;, captcha.text());
// 设置字体
//captcha.setFont(new Font(&#34;Verdana&#34;, Font.PLAIN, 32)); // 有默认字体,可以不用设置
// 设置类型,纯数字、纯字母、字母数字混合
captcha.setCharType(Captcha.TYPE_DEFAULT);
//缓存验证码
redisService.set(AuthKeys.AUTH_CAPTCHA, uuid, captcha.text().toLowerCase());
// 输出图片流
captcha.out(response.getOutputStream());
}
}3、生成验证码配置
/**
* 生成验证码配置
*
*/
@Configuration
public class KaptchaConfig {
@Bean
public DefaultKaptcha producer() {
Properties properties = new Properties();
//图片边框
properties.setProperty(&#34;kaptcha.border&#34;, &#34;no&#34;);
//文本集合,验证码值从此集合中获取
properties.setProperty(&#34;kaptcha.textproducer.char.string&#34;, &#34;ABCDEGHJKLMNRSTUWXY23456789&#34;);
//字体颜色
properties.setProperty(&#34;kaptcha.textproducer.font.color&#34;, &#34;0,84,144&#34;);
//干扰颜色
properties.setProperty(&#34;kaptcha.noise.color&#34;, &#34;0,84,144&#34;);
//字体大小
properties.setProperty(&#34;kaptcha.textproducer.font.size&#34;, &#34;30&#34;);
//背景颜色渐变,开始颜色
properties.setProperty(&#34;kaptcha.background.clear.from&#34;, &#34;247,255,234&#34;);
//背景颜色渐变,结束颜色
properties.setProperty(&#34;kaptcha.background.clear.to&#34;, &#34;247,255,234&#34;);
//图片宽
properties.setProperty(&#34;kaptcha.image.width&#34;, &#34;125&#34;);
//图片高
properties.setProperty(&#34;kaptcha.image.height&#34;, &#34;35&#34;);
properties.setProperty(&#34;kaptcha.session.key&#34;, &#34;code&#34;);
//验证码长度
properties.setProperty(&#34;kaptcha.textproducer.char.length&#34;, &#34;4&#34;);
//字体
properties.setProperty(&#34;kaptcha.textproducer.font.names&#34;, &#34;Arial,Courier,cmr10,宋体,楷体,微软雅黑&#34;);
properties.put(&#34;kaptcha.textproducer.char.space&#34;, &#34;5&#34;);
Config config = new Config(properties);
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}4、CaptchaService.java接口
public interface CaptchaService {
boolean validate(String uuid, String code);
BufferedImage getCaptcha(String uuid);
}5、CaptchaServiceImpl.java实现类
@Service
@Slf4j
public class CaptchaServiceImpl implements CaptchaService {
@Autowired
private Producer producer;
@Autowired
private RedisService redisService;
@Value(&#34;${default-captcha}&#34;)
private String defaultCaptcha;
/**
* 生成并缓存验证码,返给前端图片
*/
@Override
public BufferedImage getCaptcha(String uuid) {
if (StringUtils.isEmpty(uuid)) {
throw new GlobalException(BasicCodeMsg.PARAMETER_ERROR.setMsg(&#34;uuid不能为空&#34;));
}
//生成文字验证码
String code = producer.createText();
log.info(&#34;uuid:{},验证码:{}&#34;,uuid,code);
//缓存验证码
redisService.set(AuthKeys.AUTH_CAPTCHA, uuid, code);
return producer.createImage(code);
}
}
/**
* 校验验证码
*/
@Override
public boolean validate(String uuid, String code) {
//测试环境123456通过验证(可不加)
if (EnvEnum.dev.name().equals(env) && code.equals(defaultCaptcha)) {
return true;
}
String cacheCode = redisService.get(AuthKeys.AUTH_CAPTCHA, uuid, String.class);
if (StringUtils.isEmpty(cacheCode)) {
return false;
}
//删除缓存验证码
redisService.delete(AuthKeys.AUTH_CAPTCHA, uuid);
if (cacheCode.equalsIgnoreCase(code)) {
return true;
}
return false;
}6、增加验证码校验
在登录授权验证的地方添加验证码相关校验,也就是原来校验用户名密码的地方增加。
if (&#34;captcha&#34;.equals(type)) {
LoginVo loginVo = LoginVo.builder().captcha(captcha)
.loginName(username)
.uuid(uuid).build();
boolean result = captchaService.validate(uuid, captcha);
if (!result) {
throw new OAuth2Exception(&#34;验证码不正确&#34;);
}
return;涉及文件
 |
|