生成图片验证码

左手的ㄟ右手 2022-05-15 07:51 488阅读 0赞

问题描述

在项目中遇到有人恶意拉取图片资源,无限刷资源,导致阿里图片服务器流量暴涨(钱遭不住),使得带宽一直处于上限,正常用户不能好的访问。

解决办法

这个功能设计要点有两个:
1.接口限流使得不能一直拉取接口获取图片地址,针对同一用户单位时间内请求不能超过N次,如果超过N次属于不正常访问,ip列入黑名单
2.图片服务器处理,添加失效时间,访问图片时必须带个token
3.添加图片验证码,列入黑名单用户必须验证图片验证码才可解除黑名单,这里主要说明生成图片验证码

其他情况验证码使用:通常我们最登录的时候,为了防止多次尝试或攻击登录接口,我们需要弄一个验证码的功能,只有输入验证码正确的情况下,我们才会去做密码校验,这样就减少了密码可能会被试出来的可能。

代码实现

使用包:java.awt

生成验证码配置(CaptchaUtil工具类)

  1. /** * 验证码工具 * * @author LiRui * @version 1.0 */
  2. public final class CaptchaUtil {
  3. public static final int WIDTH = 60;
  4. public static final int HEIGHT = 20;
  5. private static final char[] MAP_TABLE = {
  6. '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6',
  7. '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
  8. 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
  9. private CaptchaUtil() {
  10. }
  11. /** * 生成图片验证码 * * @param width 宽度 * @param height 高度 * @return */
  12. public static Map<String, Object> getImageCode(int width, int height) {
  13. Map<String, Object> returnMap = new HashMap<>(2);
  14. width = (width <= 0 ? WIDTH : width);
  15. height = (height <= 0 ? HEIGHT : height);
  16. BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
  17. // 获取图形上下文
  18. Graphics g = image.getGraphics();
  19. //生成随机类
  20. Random random = new Random();
  21. // 设定背景色
  22. g.setColor(getRandColor(200, 250));
  23. g.fillRect(0, 0, width, height);
  24. //设定字体
  25. g.setFont(new Font("Times New Roman", Font.PLAIN, 18));
  26. // 随机产生168条干扰线,使图象中的认证码不易被其它程序探测到
  27. g.setColor(getRandColor(160, 200));
  28. for (int i = 0; i < 168; i++) {
  29. int x = random.nextInt(width);
  30. int y = random.nextInt(height);
  31. int xl = random.nextInt(12);
  32. int yl = random.nextInt(12);
  33. g.drawLine(x, y, x + xl, y + yl);
  34. }
  35. //取随机产生的码
  36. StringBuilder strEnsure = new StringBuilder();
  37. //4代表4位验证码,如果要生成更多位的认证码,则加大数值
  38. for (int i = 0; i < 4; ++i) {
  39. strEnsure.append(MAP_TABLE[(int) (MAP_TABLE.length * Math.random())]);
  40. // 将认证码显示到图象中
  41. g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110),
  42. 20 + random.nextInt(110)));
  43. // 直接生成
  44. String str = strEnsure.substring(i, i + 1);
  45. // 设置随便码在背景图图片上的位置
  46. g.drawString(str, 13 * i + 20, 25);
  47. }
  48. // 释放图形上下文
  49. g.dispose();
  50. String base64Img = "data:image/png;base64,";
  51. returnMap.put("image", base64Img + encodeToString("jpg", image));
  52. returnMap.put("strEnsure", strEnsure.toString());
  53. return returnMap;
  54. }
  55. /** * 给定范围获得随机颜色 */
  56. static Color getRandColor(int fc, int bc) {
  57. Random random = new Random();
  58. if (fc > 255) {
  59. fc = 255;
  60. }
  61. if (bc > 255) {
  62. bc = 255;
  63. }
  64. int r = fc + random.nextInt(bc - fc);
  65. int g = fc + random.nextInt(bc - fc);
  66. int b = fc + random.nextInt(bc - fc);
  67. return new Color(r, g, b);
  68. }
  69. /** * 将图片转换成base64格式进行存储 * * @param formatName 文件格式 * @param image 图片流 * @return base64字符串 */
  70. private static String encodeToString(String formatName, BufferedImage image) {
  71. String imageString = null;
  72. try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
  73. ImageIO.write(image, formatName, bos);
  74. byte[] imageBytes = bos.toByteArray();
  75. imageString = new String(Base64.encodeBase64(imageBytes));
  76. } catch (IOException e) {
  77. e.printStackTrace();
  78. }
  79. return imageString;
  80. }
  81. }

我这里直接将图片转换成base64位字符串,前端可以直接展示,这样写也可方便ajax请求使用

调用生成验证码返回

  1. /** * 用于生成带四位数字验证码的图片 */
  2. @RequestMapping(value = "/captcha", method = RequestMethod.GET)
  3. @ResponseBody
  4. public HashMap<String, Object> imageCode() {
  5. //返回验证码和图片的map
  6. Map<String, Object> map = CaptchaUtil.getImageCode(86, 37);
  7. final ValueOperations operations = redisTemplate.opsForValue();
  8. //生成唯一key
  9. final String verifyKey = getVerifyKey();
  10. //设置过期时间
  11. operations.set(verifyKey, map.get("strEnsure"), 5, TimeUnit.MINUTES);
  12. //返回base64位图片字符串,唯一key(用于查找哪个验证码)
  13. return JsonWrapper.successWrapper("image", map.get("image"), "key", verifyKey);
  14. }

如果只是一个单体项目,可以存在session当中,如果集成了shiro,也可以放shiro的session中。
而在分布式系统当中,需要考虑验证码的共享功能。
1、可以存储在session中,如需要集成spring session,把session存到redis等存储中间件中session验证码共享功能。
2、shiro集成了redis的,就可以存在shiro session当中实现共享。
3、当然,你也可以直接把验证码存到redis等中间件中,不需要通过session,但是key就必须唯一。(当前使用)

验证验证码

  1. /** * code验证码验证 * * @param checkCode 前端用户输入返回的验证码 * @param key 验证码唯一标识 */
  2. @RequestMapping(value = "/verify", method = RequestMethod.POST)
  3. @ResponseBody
  4. public HashMap<String, Object> checkCode(HttpServletRequest request,
  5. @RequestParam String checkCode, @RequestParam String key) {
  6. if (!redisTemplate.hasKey(key)) {
  7. return JsonWrapper.failureWrapperMsg("验证码失效,请重新获取");
  8. }
  9. final ValueOperations operations = redisTemplate.opsForValue();
  10. final String code = (String) operations.get(verifyKey);
  11. if (!code.equalsIgnoreCase(key)) {
  12. return JsonWrapper.failureWrapperMsg("验证码错误,请重新输入");
  13. }
  14. return JsonWrapper.successWrapper();
  15. }

另外吕一明老师使用了三方包实现图片验证码验证更加简洁、简单(这篇博客就是参照他的格式写的)图片验证码的需求分析、优雅实现

发表评论

表情:
评论列表 (有 0 条评论,488人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Kaptcha生成图片验证

    Kaptcha简介 kaptcha 是一个很有用的验证码生成工具。由于它是可配置的,有了它,你能够生成各种样式的验证码。 Kaptcha 是一个可高度配置的实用验证码

    相关 Java生成图片验证

    功能介绍:自定义图片尺寸和字符长度,随机背景颜色和字符颜色,随机字符偏移角度,字符平滑边缘,干扰线,噪点,背景扭曲。 VerifyCodeUtils类,生成图片流,然后不同框

    相关 生成图片验证

    问题描述 在项目中遇到有人恶意拉取图片资源,无限刷资源,导致阿里图片服务器流量暴涨(钱遭不住),使得带宽一直处于上限,正常用户不能好的访问。 解决办法 这个功能设