内容速览:
1.Kaptcha 框架介绍与配置类开发2.池化思想应⽤-Redis6.X配置连接池实战连接池好处
3.实战图形验证码接口编写
4.结果测试

Kaptcha 框架介绍 ⾕歌开源的⼀个可⾼度配置的实⽤验证码⽣成⼯具

验证码的字体/⼤⼩/颜⾊
验证码内容的范围(数字,字⺟,中⽂汉字!)
验证码图⽚的⼤⼩,边框,边框粗细,边框颜⾊
验证码的⼲扰线
验证码的样式(⻥眼样式、3D、普通模糊)

聚合⼯程依赖添加(使⽤国内baomidou⼆次封装的springboot整合starter)

<!--kaptcha依赖包-->
 <dependency>
 <groupId>com.baomidou</groupId>
 <artifactId>kaptcha-spring-bootstarter</artifactId>
 <version>1.1.0</version>
 </dependency>

配置类

@Configuration
public class CaptchaConfig {
    @Bean
    @Qualifier("captchaProducer")
    public DefaultKaptcha kaptcha() {
        DefaultKaptcha kaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
//
        properties.setProperty(Constants.KAPTCHA_BORDER, "yes");
//
        properties.setProperty(Constants.KAPTCHA_BORDER_COLOR, "220,220,220");
//
//properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "38,29,12");
//
        properties.setProperty(Constants.KAPTCHA_IMAGE_WIDTH, "147");
//
        properties.setProperty(Constants.KAPTCHA_IMAGE_HEIGHT, "34");
//
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE, "25");
//
//properties.setProperty(Constants.KAPTCHA_SESSION_KEY, "code");
        //验证码个数

        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
//
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Courier");
        //字体间隔

        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE,"8");
        //⼲扰线颜⾊
//
        properties.setProperty(Constants.KAPTCHA_NOISE_COLOR, "white");
        //⼲扰实现类

        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;
    }
}

池化思想应⽤-Redis6.X配置连接池实战连接池好处

使⽤连接池不⽤每次都⾛三次握⼿、每次都关闭Jedis相对于直连,使⽤相对麻烦,在资源管理上需要很多参数来保证,规划不合理也会出现问题
如果pool已经分配了maxActive个jedis实例,则此时pool的状态就成exhausted了

Maven

<!--redis客户端-->
 <dependency>
 
<groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-redis</artifactId>
 <exclusions>
 <exclusion>
 <groupId>io.lettuce</groupId>
 <artifactId>lettuce-core</artifactId>
 </exclusion>
 </exclusions>
 </dependency>
 <dependency>
 <groupId>redis.clients</groupId>
 <artifactId>jedis</artifactId>
 </dependency>
 <dependency>
 <groupId>org.apache.commons</groupId>
 <artifactId>commons-pool2</artifactId>
 </dependency>

配置Redis连接

redis:
    client-type: jedis
    host: 127.0.0.1
    password: 123456
    port: 6379
    jedis:
      pool:

    # 连接池最⼤连接数(使⽤负值表示没有限制)
        max-active: 100
    # 连接池中的最⼤空闲连接
        max-idle: 100
    # 连接池中的最⼩空闲连接
        min-idle: 100
    # 连接池最⼤阻塞等待时间(使⽤负值表示没有限制)
        max-wait: 60000

注意将配置里的host和password改为你自己的

序列化配置

@Configuration
public class RedisTemplateConfiguration {
    @Bean
    public RedisTemplate<Object, Object>
    redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();

        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 使⽤Jackson2JsonRedisSerialize 替换默认序列化
                Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();

        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        // 设置key和value的序列化规则
        redisTemplate.setKeySerializer(new StringRedisSerializer());

        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // 设置hashKey和hashValue的序列化规则
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());

        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        return redisTemplate;
    }
}

实战图形验证码接口编写:

@RestController
@RequestMapping("/api/v1/notify")
@Slf4j
public class NotifyController {


    public static final int CAPTCHA_CODE_EXPIRED = 60 * 1000 * 10;
    @Autowired
    private Producer captchaProduce;

  

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 图形验证码生成流程
     * 1.用captcha创建文本 String captchaText = captchaProduce.createText();
     * 2.生成文本图像  BufferedImage bufferedImage = captchaProduce.createImage(captchaText);
     * 3.用流写出 try(ServletOutputStream outputStream = response.getOutputStream();) {
     *          ImageIO.write(bufferedImage,"jpg",outputStream);
     *          outputStream.flush();
     * @param request
     * @param response
     */
    @GetMapping("captcha")
    public void getCaptcha(HttpServletRequest request, HttpServletResponse response){
        String captchaText = captchaProduce.createText();
        log.info("验证码内容{}",captchaText);
        redisTemplate.opsForValue().set(getCaptchaKey(request),captchaText,CAPTCHA_CODE_EXPIRED, TimeUnit.SECONDS);
        BufferedImage bufferedImage = captchaProduce.createImage(captchaText);
     try(ServletOutputStream outputStream = response.getOutputStream();) {
         ImageIO.write(bufferedImage,"jpg",outputStream);
         outputStream.flush();
     }catch (Exception e){
         log.info("图形验证码异常");
     }


    }
    /**
    使用用户ip加user-agent并用md5进行加密
    **/
    private String getCaptchaKey(HttpServletRequest request){
        String ip = CommonUtil.getIpAddr(request);
        String userAgent = request.getHeader("User-Agent");
        String key = "account-service:captcha:"+CommonUtil.MD5(ip+userAgent);
        log.info("key{}",key);
        return key;
    }

}

CommonUtil工具类,直接cv即可,在工作中也是这样:

@Slf4j
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)
                    {
                        log.warn("[]",e);
                    }
                    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;
    }
    /**
     * 获取全部请求头
     * @param request
     * @return
     */
    public static Map<String, String>
    getAllRequestHeader(HttpServletRequest request){
        Enumeration<String> headerNames = request.getHeaderNames();
        Map<String, String> map = new HashMap<>();
        while (headerNames.hasMoreElements()) {
            String key = (String)headerNames.nextElement();
            //根据名称获取请求头的值
            String value = request.getHeader(key);
            map.put(key,value);
        }
        return map;
    }
    /**
     * MD5加密
     *
     * @param data
     * @return
     */
    public static String MD5(String data) {
        try {
            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;
    }
    /**
     * 获取验证码随机数
     *
     * @param length
     * @return
     */
    public static String getRandomCode(int length) {
        String sources = "0123456789";
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int j = 0; j < length; j++) {

            sb.append(sources.charAt(random.nextInt(9)));
        }
        return sb.toString();
    }
    /**
     * 获取当前时间戳
     *
     * @return
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis();
    }
    /**
     * ⽣成uuid
     *
     * @return
     */
    public static String generateUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }
    /**
     * 获取随机⻓度的串
     *
     * @param length
     * @return
     */
    private static final String ALL_CHAR_NUM = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    public static String getStringNumRandom(int length) {
        //⽣成随机数字和字⺟,
        Random random = new Random();
        StringBuilder saltString = new
                StringBuilder(length);
        for (int i = 1; i <= length; ++i) {

            saltString.append(ALL_CHAR_NUM.charAt(random.nextInt(ALL_CHAR_NUM.length())));
        }
        return saltString.toString();
    }
    /**
     * 响应json数据给前端
     *
     * @param response
     * @param obj
     */
    public static void
    sendJsonMessage(HttpServletResponse response, Object obj) {
        response.setContentType("application/json;charset=utf-8");
        try (PrintWriter writer =
                     response.getWriter()) {
            writer.print(JsonUtil.obj2Json(obj));
            response.flushBuffer();
        } catch (IOException e) {
            log.warn("响应json数据给前端异常:{}",e);
        }
    }

}

浏览器访问结果如下:

springboot滑动验证码 动物 springboot 图形验证码_springboot滑动验证码 动物


同时控制台打印:

springboot滑动验证码 动物 springboot 图形验证码_springboot滑动验证码 动物_02


redis可视化工具查看:

springboot滑动验证码 动物 springboot 图形验证码_spring boot_03