String数据结构之验证码实战

验证码实现我们采用Kaptcha 框架,它是谷歌开源的一个可高度配置的实用验证码生成工具。

我们这次实现的验证码思路是:用户访问注册页面时,我们先将图形验证码在服务端生成,并存到redis中,再将该图形验证码返回给用户界面,用户输入图形验证码的值之后,点击"发送验证码"的同时,也会将电话号码以及图形验证码也一同带到服务端,服务端把接收到的图形验证码跟redis中图形验证码是否一致。

首先我们要添加kaptcha依赖包:

复制代码
<dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>kaptcha-spring-boot-starter</artifactId>
      <version>1.0.0</version>
    </dependency>

验证码的配置类:

复制代码
@Configuration
public class CaptchaConfig {
​
    /**
     * 验证码配置
     * Kaptcha配置类名
     * 
     * @return
     */
    @Bean
    @Qualifier("captchaProducer")
    public DefaultKaptcha kaptcha() {
        DefaultKaptcha kaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        //验证码个数
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
        //字体间隔
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE,"8");
        //干扰线颜色
​
        //干扰实现类
        properties.setProperty(Constants.KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
​
        //图片样式
        properties.setProperty(Constants.KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.WaterRipple");
​
        //文字来源
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_STRING, "0123456789");
        Config config = new Config(properties);
        kaptcha.setConfig(config);
        return kaptcha;
    }
}

配置CommonUtil工具类:

复制代码
public class CommonUtil {
    /**
     * 获取ip
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) {
                // "***.***.***.***".length()
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress="";
        }
        return ipAddress;
    }

    public static String MD5(String data)  {
        try {
            java.security.MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] array = md.digest(data.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
            }
            return sb.toString().toUpperCase();
        } catch (Exception exception) {
        }
        return null;
    }
}

还有配置JsonData响应工具类,方便我们查看结果:

复制代码
public class JsonData {
    /**
     * 状态码 0 表示成功
     */

    private Integer code;
    /**
     * 数据
     */
    private Object data;
    /**
     * 描述
     */
    private String msg;

    public JsonData(int code,Object data,String msg){
        this.code = code;
        this.data = data;
        this.msg = msg;
    }

    /**
     * 成功,不传入数据
     * @return
     */
    public static JsonData buildSuccess() {
        return new JsonData(0, null, null);
    }

    /**
     *  成功,传入数据
     * @param data
     * @return
     */
    public static JsonData buildSuccess(Object data) {
        return new JsonData(0, data, null);
    }

    /**
     * 失败,传入描述信息
     * @param msg
     * @return
     */
    public static JsonData buildError(String msg) {
        return new JsonData(-1, null, msg);
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

接下来我们就可以进行接口的开发:

复制代码
@RestController
@RequestMapping("/api/v1/captcha")
public class CaptchaController {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private Producer captchaProducer;

    /**
     * 获取图形验证码
     * @param request
     * @param response
     */
    @GetMapping("get_captcha")
    public void getCaptcha(HttpServletRequest request, HttpServletResponse response){
        String captchaText = captchaProducer.createText();
        String key = getCaptchaKey(request);
        stringRedisTemplate.opsForValue().set(key,captchaText,10, TimeUnit.MINUTES);
        BufferedImage bufferedImage = captchaProducer.createImage(captchaText);
        ServletOutputStream servletOutputStream = null;
        try {
            servletOutputStream = response.getOutputStream();
            ImageIO.write(bufferedImage,"jpg",servletOutputStream);
            servletOutputStream.flush();
            servletOutputStream.close();
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    /**
     * 发送验证码
     * @param to
     * @param captcha
     * @param request
     * @return
     */
    @GetMapping("send_code")
    public JsonData sendCode(@RequestParam(value = "to",required = true)String to,
                             @RequestParam(value = "captcha",required = true)String captcha,
                             HttpServletRequest request){
        String key = getCaptchaKey(request);
        String cacheCaptcha  = stringRedisTemplate.opsForValue().get(key);
        if(captcha!=null && cacheCaptcha!=null && cacheCaptcha.equalsIgnoreCase(captcha)){
            stringRedisTemplate.delete(key);
            //TODO 发送验证码
            return JsonData.buildSuccess();
        }else {
            return JsonData.buildError("验证码错误");
        }

    }


    /**
     * 获取缓存的key
     * @param request
     * @return
     */
    private String getCaptchaKey(HttpServletRequest request){
        String ip = CommonUtil.getIpAddr(request);
        String userAgent = request.getHeader("User-Agent");
        String key = "user-service:captcha:"+CommonUtil.MD5(ip+userAgent);
        return key;

    }
}
相关推荐
阿维的博客日记2 小时前
从夯到拉的Redis和MySQL双写一致性解决方案排名
redis·分布式·mysql
好玩的Matlab(NCEPU)2 小时前
Redis vs RabbitMQ 对比总结
数据库·redis·rabbitmq
en-route2 小时前
基于 Redis 的基数统计:高效的大规模去重与计数
redis·基数统计
深圳蔓延科技2 小时前
Redis,什么是缓存穿透/击穿/雪崩,如何解决它们
redis
蹦跑的蜗牛3 小时前
Spring Boot使用Redis实现消息队列
spring boot·redis·后端
馍馍菜5 小时前
Redis Insight黑屏,页面空白
redis·redis insight
你想考研啊9 小时前
二、redis集群部署(3主3从)
数据库·redis·缓存
顾漂亮9 小时前
Redis深度探索
java·redis·后端·spring·缓存
你想考研啊12 小时前
一、redis安装(单机)和使用
前端·数据库·redis
洲覆12 小时前
Redis 驱动适配 Reactor 模式
开发语言·网络·数据库·redis