feat: 增加获取图片验证码接口

This commit is contained in:
xiongxiaoyang 2022-05-17 17:08:18 +08:00
parent 7c2cd61af6
commit 0fb2459a0a
8 changed files with 297 additions and 0 deletions

View File

@ -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<String> getImgVerifyCode(HttpServletRequest request) throws IOException {
return RestResp.ok(fileService.getImgVerifyCode(IpUtils.getRealIp(request)));
}
}

View File

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

View File

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

View File

@ -52,6 +52,11 @@ public class ApiRouterConsts {
* */ * */
public static final String USER_URL_PREFIX = "/user"; public static final String USER_URL_PREFIX = "/user";
/**
* 文件模块请求路径前缀
* */
public static final String FILE_URL_PREFIX = "/file";
/** /**
* 前台门户首页API请求路径前缀 * 前台门户首页API请求路径前缀
*/ */
@ -72,4 +77,9 @@ public class ApiRouterConsts {
*/ */
public static final String API_FRONT_USER_URL_PREFIX = API_FRONT_URL_PREFIX + USER_URL_PREFIX; 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;
} }

View File

@ -79,6 +79,10 @@ public class CacheConsts {
* */ * */
public static final String LAST_UPDATE_BOOK_ID_LIST_CACHE_NAME = "lastUpdateBookIdListCache"; 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::";
/** /**
* 缓存配置常量 * 缓存配置常量

View File

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

View File

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

View File

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