# SpringBoot集成验证码 **Repository Path**: jianml/captcha ## Basic Information - **Project Name**: SpringBoot集成验证码 - **Description**: SpringBoot集成验证码 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2020-01-07 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # SpringBoot集成验证码 ## 介绍 图形验证码是最简单的人机校验手段,可以有效的防止恶意程序暴力破解登录密码。我们通常会把验证码存储在Redis中,并设置失效时间(本次教程没有集成Redis模块)。 生成图形验证码可以借助GitHub的一个开源项目EasyCaptcha,其提供了较为丰富的验证码配置可供选择 > 参考:https://github.com/whvcse/EasyCaptcha ## 创建验证码配置类 在编写验证码生成服务之前,我们先创建一个验证码配置类,用于定义个性化验证码配置。在properties目录下新建 `CaptchaProperties`验证码配置类 ```java @Data @Configuration @PropertySource(value = "classpath:captcha.properties") @ConfigurationProperties(prefix = "captcha") public class CaptchaProperties { /** * 验证码有效时间,单位秒 */ private Long time = 120L; /** * 验证码类型,可选值 png和 gif */ private String type = "png"; /** * 图片宽度,px */ private Integer width = 130; /** * 图片高度,px */ private Integer height = 48; /** * 验证码位数 */ private Integer length = 4; /** * 验证码值的类型 * 1. 数字加字母 * 2. 纯数字 * 3. 纯字母 */ private Integer charType = 2; } ``` 最后在resources目录下的`captcha.properties`配置文件里添加验证码配置 ```properties captcha.time=120 captcha.type=png captcha.width=115 captcha.height=42 captcha.length=4 captcha.charType=2 ``` 验证码配置相关代码编写完毕后,我们开始编写生成验证码服务代码 ## 创建验证服务类 接着在.service路径下新建`CaptchaService`验证码服务类 ```java @Service public class CaptchaServiceImpl implements CaptchaService { @Autowired private CaptchaProperties properties; @Override public void create(HttpServletRequest request, HttpServletResponse response) throws Exception { String key = request.getParameter("key"); if(StringUtils.isBlank(key)) throw new Exception("验证码key不能为空"); setHeader(response, properties.getType()); Captcha captcha = createCaptcha(); // TODO: 缓存到redis中 captcha.out(response.getOutputStream()); } @Override public void check(String key, String value) throws Exception { // TODO:获取redis中的验证码 String redisCode = "1234"; if(StringUtils.isBlank(value)) throw new Exception("请输入验证码"); if(redisCode == null) throw new Exception("验证码已过期"); if(!StringUtils.equalsIgnoreCase(value, redisCode)) throw new Exception("验证码不正确"); } private Captcha createCaptcha() { Captcha captcha = null; if (StringUtils.equalsIgnoreCase(properties.getType(), "gif")) { captcha = new GifCaptcha(properties.getWidth(), properties.getHeight(), properties.getLength()); } else { captcha = new SpecCaptcha(properties.getWidth(), properties.getHeight(), properties.getLength()); } captcha.setCharType(properties.getCharType()); return captcha; } private void setHeader(HttpServletResponse response, String type){ if (StringUtils.equalsIgnoreCase(type, "gif")) { response.setContentType(MediaType.IMAGE_GIF_VALUE); } else { response.setContentType(MediaType.IMAGE_PNG_VALUE); } response.setHeader(HttpHeaders.PRAGMA, "No-cache"); response.setHeader(HttpHeaders.CACHE_CONTROL, "No-cache"); response.setDateHeader(HttpHeaders.EXPIRES, 0L); } } ``` 上面包含4个方法,这4个方法含义如下: 1. `createCaptcha`方法通过验证码配置文件`CaptchaProperties`生成相应的验证码,比如PNG格式的或者GIF格式的,验证码图片的长宽高,验证码字符的类型(纯数字,纯字母或者数字字母组合),验证码字符的长度等; 2. `setHeader`用于设置响应头。在生成验证码图片后我们需要将其返回到客户端,所以需要根据不同的验证码格式设置不同的响应头; 3. `create`方法用于生成验证码。在前后端不分离的架构下,我们通过浏览器传输的jsessionid来和验证码图片一一对应,但前后的分离的模式下,客户端发送的请求并没有携带jsessionid(因为不再基于Session),所以我们需要客户端在发送获取验证码请求的时候,携带一个key(比如按一定算法生成的随机字符串,模拟jsessionid)来和验证码一一对应。于是我们在`create`里一开始就从请求中获取key值,然后根据验证码配置文件生成验证码,并将验证码字符保存到了Redis中,并将验证码图片以流的形式返回给客户端。 4. `check`用于校验验证码,逻辑很简单,就是根据客户端上送的key,从Redis中取出相应的验证码,然后和用户输入的验证码进行比较。 ## 测试 创建好验证码服务类后,我们编写一个REST接口,用于客户端调用生成验证码 ```java @RestController public class CaptchaController { @Autowired private CaptchaService captchaService; @GetMapping("/captcha") public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception { captchaService.create(request, response); } @PostMapping("/login") public String login(String username, String password, String code, String key){ // 省略了用户账号信息的验证逻辑... try { captchaService.check(key, code); return "验证成功"; }catch (Exception e){ // 可以用全局异常处理来优化此处代码 return e.getMessage(); } } } ``` 在浏览器中获取验证码 ![mark](http://image.jianml.cn/image/20200107/zUmNA8WxfasT.png) > 源码地址:https://gitee.com/jianml/captcha