From 0fb2459a0aefd9a0e04bf42c4028a965e5b5c11e Mon Sep 17 00:00:00 2001 From: xiongxiaoyang <773861846@qq.com> Date: Tue, 17 May 2022 17:08:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E9=AA=8C=E8=AF=81=E7=A0=81=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/front/FileController.java | 36 ++++++ .../core/common/util/ImgVerifyCodeUtils.java | 112 ++++++++++++++++++ .../novel/core/common/util/IpUtils.java | 43 +++++++ .../novel/core/constant/ApiRouterConsts.java | 10 ++ .../novel/core/constant/CacheConsts.java | 4 + .../novel/manager/VerifyCodeManager.java | 47 ++++++++ .../xxyopen/novel/service/FileService.java | 19 +++ .../novel/service/impl/FileServiceImpl.java | 26 ++++ 8 files changed, 297 insertions(+) create mode 100644 src/main/java/io/github/xxyopen/novel/controller/front/FileController.java create mode 100644 src/main/java/io/github/xxyopen/novel/core/common/util/ImgVerifyCodeUtils.java create mode 100644 src/main/java/io/github/xxyopen/novel/core/common/util/IpUtils.java create mode 100644 src/main/java/io/github/xxyopen/novel/manager/VerifyCodeManager.java create mode 100644 src/main/java/io/github/xxyopen/novel/service/FileService.java create mode 100644 src/main/java/io/github/xxyopen/novel/service/impl/FileServiceImpl.java diff --git a/src/main/java/io/github/xxyopen/novel/controller/front/FileController.java b/src/main/java/io/github/xxyopen/novel/controller/front/FileController.java new file mode 100644 index 0000000..8339dca --- /dev/null +++ b/src/main/java/io/github/xxyopen/novel/controller/front/FileController.java @@ -0,0 +1,36 @@ +package io.github.xxyopen.novel.controller.front; + +import io.github.xxyopen.novel.core.common.resp.RestResp; +import io.github.xxyopen.novel.core.common.util.IpUtils; +import io.github.xxyopen.novel.core.constant.ApiRouterConsts; +import io.github.xxyopen.novel.service.FileService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; + +/** + * 文件相关 控制器 + * + * @author xiongxiaoyang + * @date 2022/5/17 + */ +@RestController +@RequestMapping(ApiRouterConsts.API_FRONT_FILE_URL_PREFIX) +@RequiredArgsConstructor +public class FileController { + + private final FileService fileService; + + /** + * 获取图片验证码接口 + */ + @GetMapping("imgVerifyCode") + public RestResp getImgVerifyCode(HttpServletRequest request) throws IOException { + return RestResp.ok(fileService.getImgVerifyCode(IpUtils.getRealIp(request))); + } + +} diff --git a/src/main/java/io/github/xxyopen/novel/core/common/util/ImgVerifyCodeUtils.java b/src/main/java/io/github/xxyopen/novel/core/common/util/ImgVerifyCodeUtils.java new file mode 100644 index 0000000..073a3cb --- /dev/null +++ b/src/main/java/io/github/xxyopen/novel/core/common/util/ImgVerifyCodeUtils.java @@ -0,0 +1,112 @@ +package io.github.xxyopen.novel.core.common.util; + +import lombok.experimental.UtilityClass; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Base64; +import java.util.Random; + +/** + * 图片验证码工具类 + * + * @author xiongxiaoyang + * @date 2022/5/17 + */ +@UtilityClass +public class ImgVerifyCodeUtils { + + /** + * 随机产生只有数字的字符串 + */ + private final String randNumber = "0123456789"; + + /** + * 图片宽 + */ + private final int width = 100; + + /** + * 图片高 + */ + private final int height = 38; + + private final Random random = new Random(); + + /** + * 获得字体 + */ + private Font getFont() { + return new Font("Fixed", Font.PLAIN, 23); + } + + + /** + * 生成校验码图片 + */ + public String genVerifyCodeImg(String verifyCode) throws IOException { + // BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类 + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR); + // 产生Image对象的Graphics对象,改对象可以在图像上进行各种绘制操作 + Graphics g = image.getGraphics(); + //图片大小 + g.fillRect(0, 0, width, height); + //字体大小 + //字体颜色 + g.setColor(new Color(204, 204, 204)); + // 绘制干扰线 + // 干扰线数量 + int lineSize = 40; + for (int i = 0; i <= lineSize; i++) { + drawLine(g); + } + // 绘制随机字符 + drawString(g, verifyCode); + g.dispose(); + //将图片转换成Base64字符串 + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + ImageIO.write(image, "JPEG", stream); + return Base64.getEncoder().encodeToString(stream.toByteArray()); + } + + /** + * 绘制字符串 + */ + private void drawString(Graphics g, String verifyCode) { + for (int i = 1; i <= verifyCode.length(); i++) { + g.setFont(getFont()); + g.setColor(new Color(random.nextInt(101), random.nextInt(111), random + .nextInt(121))); + g.translate(random.nextInt(3), random.nextInt(3)); + g.drawString(String.valueOf(verifyCode.charAt(i - 1)), 13 * i, 23); + } + } + + /** + * 绘制干扰线 + */ + private void drawLine(Graphics g) { + int x = random.nextInt(width); + int y = random.nextInt(height); + int xl = random.nextInt(13); + int yl = random.nextInt(15); + g.drawLine(x, y, x + xl, y + yl); + } + + /** + * 获取随机的校验码 + */ + public String getRandomVerifyCode(int num) { + int randNumberSize = randNumber.length(); + StringBuilder verifyCode = new StringBuilder(); + for (int i = 0; i < num; i++) { + String rand = String.valueOf(randNumber.charAt(random.nextInt(randNumberSize))); + verifyCode.append(rand); + } + return verifyCode.toString(); + } + +} diff --git a/src/main/java/io/github/xxyopen/novel/core/common/util/IpUtils.java b/src/main/java/io/github/xxyopen/novel/core/common/util/IpUtils.java new file mode 100644 index 0000000..6d5f2f2 --- /dev/null +++ b/src/main/java/io/github/xxyopen/novel/core/common/util/IpUtils.java @@ -0,0 +1,43 @@ +package io.github.xxyopen.novel.core.common.util; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.experimental.UtilityClass; + +/** + * @author xiongxiaoyang + * @date 2022/5/17 + */ +@UtilityClass +public class IpUtils { + + private static final String UNKNOWN_IP = "unknown"; + + private static final String IP_SEPARATOR = ","; + + /** + * 获取真实IP + * @return 真实IP + */ + public String getRealIp(HttpServletRequest request) { + // 这个一般是Nginx反向代理设置的参数 + String ip = request.getHeader("X-Real-IP"); + if (ip == null || ip.length() == 0 || UNKNOWN_IP.equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || UNKNOWN_IP.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN_IP.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN_IP.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + // 处理多IP的情况(只取第一个IP) + if (ip != null && ip.contains(IP_SEPARATOR)) { + String[] ipArray = ip.split(IP_SEPARATOR); + ip = ipArray[0]; + } + return ip; + } +} diff --git a/src/main/java/io/github/xxyopen/novel/core/constant/ApiRouterConsts.java b/src/main/java/io/github/xxyopen/novel/core/constant/ApiRouterConsts.java index a440bae..ebbbd6a 100644 --- a/src/main/java/io/github/xxyopen/novel/core/constant/ApiRouterConsts.java +++ b/src/main/java/io/github/xxyopen/novel/core/constant/ApiRouterConsts.java @@ -52,6 +52,11 @@ public class ApiRouterConsts { * */ public static final String USER_URL_PREFIX = "/user"; + /** + * 文件模块请求路径前缀 + * */ + public static final String FILE_URL_PREFIX = "/file"; + /** * 前台门户首页API请求路径前缀 */ @@ -72,4 +77,9 @@ public class ApiRouterConsts { */ public static final String API_FRONT_USER_URL_PREFIX = API_FRONT_URL_PREFIX + USER_URL_PREFIX; + /** + * 前台门户文件相关API请求路径前缀 + */ + public static final String API_FRONT_FILE_URL_PREFIX = API_FRONT_URL_PREFIX + FILE_URL_PREFIX; + } diff --git a/src/main/java/io/github/xxyopen/novel/core/constant/CacheConsts.java b/src/main/java/io/github/xxyopen/novel/core/constant/CacheConsts.java index 0c80e70..2a294c1 100644 --- a/src/main/java/io/github/xxyopen/novel/core/constant/CacheConsts.java +++ b/src/main/java/io/github/xxyopen/novel/core/constant/CacheConsts.java @@ -79,6 +79,10 @@ public class CacheConsts { * */ public static final String LAST_UPDATE_BOOK_ID_LIST_CACHE_NAME = "lastUpdateBookIdListCache"; + /** + * 图片验证码缓存 KEY + * */ + public static final String IMG_VERIFY_CODE_CACHE_KEY = REDIS_CACHE_PREFIX + "imgVerifyCodeCache::"; /** * 缓存配置常量 diff --git a/src/main/java/io/github/xxyopen/novel/manager/VerifyCodeManager.java b/src/main/java/io/github/xxyopen/novel/manager/VerifyCodeManager.java new file mode 100644 index 0000000..ec467d6 --- /dev/null +++ b/src/main/java/io/github/xxyopen/novel/manager/VerifyCodeManager.java @@ -0,0 +1,47 @@ +package io.github.xxyopen.novel.manager; + +import io.github.xxyopen.novel.core.common.util.ImgVerifyCodeUtils; +import io.github.xxyopen.novel.core.constant.CacheConsts; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.time.Duration; +import java.util.Objects; + +/** + * 验证码 管理类 + * + * @author xiongxiaoyang + * @date 2022/5/12 + */ +@Component +@RequiredArgsConstructor +@Slf4j +public class VerifyCodeManager { + + private final StringRedisTemplate stringRedisTemplate; + + /** + * 生成图片验证码,并放入缓存中 + */ + public String genImgVerifyCode(String userKey) throws IOException { + String verifyCode = ImgVerifyCodeUtils.getRandomVerifyCode(4); + String img = ImgVerifyCodeUtils.genVerifyCodeImg(verifyCode); + stringRedisTemplate.opsForValue().set(CacheConsts.IMG_VERIFY_CODE_CACHE_KEY + userKey + , verifyCode, Duration.ofMinutes(5)); + return img; + } + + /** + * 校验图片验证码 + */ + public boolean imgVerifyCodeOk(String userKey, String verifyCode) { + return Objects.equals( + stringRedisTemplate.opsForValue().get(CacheConsts.IMG_VERIFY_CODE_CACHE_KEY + userKey) + ,verifyCode); + } + +} diff --git a/src/main/java/io/github/xxyopen/novel/service/FileService.java b/src/main/java/io/github/xxyopen/novel/service/FileService.java new file mode 100644 index 0000000..4dd71fb --- /dev/null +++ b/src/main/java/io/github/xxyopen/novel/service/FileService.java @@ -0,0 +1,19 @@ +package io.github.xxyopen.novel.service; + +import java.io.IOException; + +/** + * 文件相关服务类 + * + * @author xiongxiaoyang + * @date 2022/5/17 + */ +public interface FileService { + + /** + * 获取图片验证码 + * @param userKey 请求用户的标识,表明该验证码属于谁 + * @return Base64编码的图片 + * */ + String getImgVerifyCode(String userKey) throws IOException; +} diff --git a/src/main/java/io/github/xxyopen/novel/service/impl/FileServiceImpl.java b/src/main/java/io/github/xxyopen/novel/service/impl/FileServiceImpl.java new file mode 100644 index 0000000..23dc07e --- /dev/null +++ b/src/main/java/io/github/xxyopen/novel/service/impl/FileServiceImpl.java @@ -0,0 +1,26 @@ +package io.github.xxyopen.novel.service.impl; + +import io.github.xxyopen.novel.manager.VerifyCodeManager; +import io.github.xxyopen.novel.service.FileService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +/** + * 文件相关服务实现类 + * + * @author xiongxiaoyang + * @date 2022/5/17 + */ +@Service +@RequiredArgsConstructor +public class FileServiceImpl implements FileService { + + private final VerifyCodeManager verifyCodeManager; + + @Override + public String getImgVerifyCode(String userKey) throws IOException { + return verifyCodeManager.genImgVerifyCode(userKey); + } +}