SpringBoot实战系列之图形验证码开发并池化Redis6存储

大家好,我是工藤学编程 🦉一个正在努力学习的小博主,期待你的关注
作业侠系列最新文章😉Java实现聊天程序
SpringBoot实战系列🐷SpringBoot实战系列之图形验证码开发并池化Redis6缓存
一起刷算法与数据结构最新文章🐷一起刷算法与数据结构-树篇1
环境搭建大集合环境搭建大集合(持续更新)

在本栏中,我们之前已经完成了:
SpringBoot实战系列之发送短信验证码
SpringBoot实战系列之从Async组件应用实战到ThreadPoolTaskExecutor⾃定义线程池


内容速览:
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了

Redis6部署请见环境搭建大集合

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

}

浏览器访问结果如下:
在这里插入图片描述
同时控制台打印:
在这里插入图片描述
redis可视化工具查看:
在这里插入图片描述本篇完!

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 26
    评论
好的,下面是实现步骤: 1. 添加 Redis 依赖 在 `pom.xml` 文件中添加 Redis 依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 2. 配置 Redis 在 `application.properties` 文件中添加 Redis 相关配置: ```properties # Redis配置 spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password= spring.redis.database=0 spring.redis.timeout=60000 ``` 3. 编写验证码生成和存储逻辑 编写一个 `VerificationCodeUtil` 工具类,生成验证码并将验证码存储Redis 中: ```java @Component public class VerificationCodeUtil { @Autowired private RedisTemplate<String, String> redisTemplate; /** * 生成验证码 * @param key 键 * @param expire 过期时间 * @return 验证码 */ public String generateCode(String key, long expire) { //生成四位数字的验证码 String code = String.format("%04d", new Random().nextInt(9999)); //将验证码存储RedisredisTemplate.opsForValue().set(key, code, expire, TimeUnit.SECONDS); return code; } } ``` 4. 编写验证码登录逻辑 编写一个 `LoginController` 控制器,实现验证码登录功能: ```java @RestController public class LoginController { @Autowired private VerificationCodeUtil verificationCodeUtil; @Autowired private RedisTemplate<String, String> redisTemplate; @PostMapping("/login") public String login(String phone, String code) { //从 Redis 中获取验证码 String cacheCode = redisTemplate.opsForValue().get(phone); if (cacheCode == null) { return "验证码已过期,请重新获取"; } if (!cacheCode.equals(code)) { return "验证码错误"; } //验证码验证通过,执行登录逻辑 return "登录成功"; } @GetMapping("/getCode") public String getCode(String phone) { //生成验证码存储Redis 中,有效期为60秒 String code = verificationCodeUtil.generateCode(phone, 60); return code; } } ``` 至此,我们已经实现了使用 Redis 存储验证码并实现验证码登录的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

工藤学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值