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;

    }
}
相关推荐
小钻风336612 小时前
Redis初阶学习
数据库·redis·缓存
赵得C14 小时前
Spring Boot 实现数据库表变更监听的 Redis 消息队列方案
数据库·spring boot·redis·监听
该用户已不存在17 小时前
Redis到底什么,该怎么用
数据库·redis·后端
知彼解己1 天前
Redis 集群模式与高可用机制
数据库·redis·缓存
海梨花1 天前
字节跳动后端 一面凉经
java·redis·学习·leetcode·面经
爱隐身的官人1 天前
新后端漏洞(上)- Redis 4.x5.x 未授权访问漏洞
redis·未授权访问漏洞
Kookoos1 天前
多租户配额与预算:限额、配额周期与突发桶的结算模型(Final)
redis·令牌桶·abp vnext·配额·突发桶·账期结算
洛洛呀。1 天前
Redis基础概述
数据库·redis·缓存
zzhongcy1 天前
Valkey vs Redis详解
数据库·redis·缓存
技术小泽1 天前
Redis-底层数据结构篇
数据结构·数据库·redis·后端·性能优化