前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 实战系列·Kaptcha 与数学公式验证码

Java 实战系列·Kaptcha 与数学公式验证码

作者头像
数媒派
发布2022-12-01 14:58:55
6090
发布2022-12-01 14:58:55
举报
文章被收录于专栏:产品优化

Kaptcha 与数学公式验证码

kaptcha 实现验证码

Google 的 kaptcha 框架是一个高度可配置的实用验证码生成工具,官方地址:kaptcha github

Spring 中引入非常简单,首先添加依赖:

代码语言:javascript
复制
<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>

配置文件:

代码语言:javascript
复制
@Configuration
public class KaptchaConfig {
    @Bean
    public DefaultKaptcha getDefaultKaptcha() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        // 图片边框
        properties.put("kaptcha.border", "yes");
        // 字体颜色
        properties.put("kaptcha.textproducer.font.color", "black");
        // 图片宽
        properties.put("kaptcha.image.width", "120");
        // 图片高
        properties.put("kaptcha.image.height", "40");
        // 字体大小
        properties.put("kaptcha.textproducer.font.size", "25");
        // 验证码长度
        properties.put("kaptcha.textproducer.char.space", "4");
        // 字体
        properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

食用:

代码语言:javascript
复制
@Controller
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class KaptchaController {

    private final DefaultKaptcha captchaProducer;

    @GetMapping("/code")
    public void defaultKaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        byte[] captchaOutputStream = null;
        ByteArrayOutputStream imgOutputStream = new ByteArrayOutputStream();
        try {
            //生产验证码字符串并保存到session中
            String verifyCode = captchaProducer.createText();
            httpServletRequest.getSession().setAttribute("verifyCode", verifyCode);
            BufferedImage challenge = captchaProducer.createImage(verifyCode);
            ImageIO.write(challenge, "jpg", imgOutputStream);
        } catch (IllegalArgumentException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        captchaOutputStream = imgOutputStream.toByteArray();
        httpServletResponse.setHeader("Cache-Control", "no-store");
        httpServletResponse.setHeader("Pragma", "no-cache");
        httpServletResponse.setDateHeader("Expires", 0);
        httpServletResponse.setContentType("image/jpeg");
        ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
        responseOutputStream.write(captchaOutputStream);
        responseOutputStream.flush();
        responseOutputStream.close();
    }

    @GetMapping("/verify")
    @ResponseBody
    public String verify(@RequestParam("code") String code, HttpSession session) {
        if (StringUtils.isEmpty(code)) {
            return "验证码不能为空";
        }
        String kaptchaCode = session.getAttribute("verifyCode") + "";
        if (StringUtils.isEmpty(kaptchaCode) || !code.equals(kaptchaCode)) {
            return "验证码错误";
        }
        return "验证成功";
    }
}

数学公式验证码

引入 redis 依赖并添加工具类:

代码语言:javascript
复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

RedisUtil 工具类:

代码语言:javascript
复制
@SuppressWarnings("unchecked")
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class RedisUtil {

    private final RedisTemplate redisTemplate;

    //=============================common============================

    /**
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间(秒)
     * @return boolean
     */
    public boolean expire(String key, long time) {
        try {
            if(time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据key获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     * @param key 键
     * @return true存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     * @param key 可以传一个值 或多个
     */
    public void del(String ...key){
        if(key != null && key.length > 0){
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

    //============================String=============================

    /**
     * 普通缓存获取
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     * @param key 键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 普通缓存放入并设置时间
     * @param key 键
     * @param value 值
     * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if(time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

验证码生成服务 GenerateVerifyCodeService:

代码语言:javascript
复制
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class GenerateVerifyCodeService {

    private final RedisUtil redisUtil;

    private String LOGIN_CODE = "LOGIN_CODE_%s";

    /**
     * 创建验证码
     *
     * @param username 用户
     */
    public BufferedImage createVerifyCode(String username) {
        int width = 80;
        int height = 32;
        // 创建图片
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();
        // 图片背景颜色
        g.setColor(new Color(0xDCDCDC));
        g.fillRect(0, 0, width, height);
        // 绘制边框
        g.setColor(Color.black);
        g.drawRect(0, 0, width - 1, height - 1);
        // 随机
        Random rdm = new Random();
        // 随便画一些线
        for (int i = 0; i < 50; i++) {
            int x = rdm.nextInt(width);
            int y = rdm.nextInt(height);
            g.drawOval(x, y, 0, 0);
        }
        // 生成验证码
        String verifyCode = generateVerifyCode(rdm);
        g.setColor(new Color(0, 100, 0));
        g.setFont(new Font("Candara", Font.BOLD, 18));
        g.drawString(verifyCode.replace("*", "x"), 8, 24);
        g.dispose();
        // 把验证码存到redis中
        int rnd = calc(verifyCode);
        // 记录数学公司的结果
        redisUtil.set(String.format(LOGIN_CODE, username), String.valueOf(rnd), 60);
        //输出图片
        return image;
    }

    /**
     * 验证验证码是否正确
     *
     * @param username   账号
     * @param verifyCode 验证码
     */
    public boolean checkVerifyCode(String username, int verifyCode) {
        if (username == null) {
            return false;
        }
        String redisKey = String.format(LOGIN_CODE, username);
        String codeOld = (String) redisUtil.get(redisKey);
        if (codeOld == null || Integer.parseInt(codeOld) - verifyCode != 0) {
            return false;
        }
        redisUtil.del(redisKey);
        return true;
    }


    /**
     * 传入数学公式,得到结果
     *
     * @param exp 比如 1+2+3
     * @return 6
     */
    private static int calc(String exp) {
        try {
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("JavaScript");
            return (Integer) engine.eval(exp);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    private static char[] ops = new char[]{'+', '-', '*'};

    /**
     * 随机验证码
     * + - *
     */
    private String generateVerifyCode(Random rdm) {
        int num1 = rdm.nextInt(10);
        int num2 = rdm.nextInt(10);
        int num3 = rdm.nextInt(10);
        char op1 = ops[rdm.nextInt(3)];
        char op2 = ops[rdm.nextInt(3)];
        return "" + num1 + op1 + num2 + op2 + num3;
    }
}

食用:

代码语言:javascript
复制
@Controller
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class KaptchaController {

  private final GenerateVerifyCodeService verifyCodeService;

  /**
    * 获取验证码
    */
  @GetMapping("/verifyCode")
  @ResponseBody
  public String verifyCode(
          HttpServletResponse response,
          @RequestParam(value = "username", required = true) String username
  ) {
      try {
          BufferedImage image = this.verifyCodeService.createVerifyCode(username);
          OutputStream out = response.getOutputStream();
          ImageIO.write(image, "JPEG", out);
          out.flush();
          out.close();
          return null;
      } catch (Exception e) {
          e.printStackTrace();
          return "验证码生成失败";
      }
  }

  /**
    * 登录验证
    */
  @PostMapping("/login")
  @ResponseBody
  public String login(
          @RequestParam(value = "username", required = true) String username,
          @RequestParam(value = "password", required = true) String password,
          @RequestParam(value = "code", required = true) Integer code
  ) {
      if (StringUtils.isEmpty(code)) {
          return "验证码不能为空";
      }
      boolean checkVerifyCode = this.verifyCodeService.checkVerifyCode(username, code);
      if (!checkVerifyCode) {
          return "验证码错误";
      }
      return "验证成功";
  }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Kaptcha 与数学公式验证码
    • kaptcha 实现验证码
      • 数学公式验证码
      相关产品与服务
      验证码
      腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档