feat: 图片旋转验证

This commit is contained in:
zlg
2024-05-10 18:50:12 +08:00
parent 96cc5947a1
commit 7992390afd
8 changed files with 300 additions and 93 deletions

View File

@@ -23,7 +23,6 @@ public class SmsInitConfig {
List<SchisandraSmsConfigBO> SchisandraSmsConfigBOs= schisandraSmsConfigDomainService.queryAll();
List<SchisandraSmsConfigDTO> schisandraSmsConfigDTOS = SchisandraSmsConfigDTOConvert.INSTANCE.convertBOToDTOList(SchisandraSmsConfigBOs);
for (SchisandraSmsConfigDTO schisandraSmsConfig : schisandraSmsConfigDTOS){
System.out.println(schisandraSmsConfig.toString());
if (schisandraSmsConfig!=null){
// 创建SmsBlend 短信实例
SmsFactory.createSmsBlend(smsReadConfig);

View File

@@ -2,6 +2,12 @@ package com.schisandra.auth.application.controller;
import cn.hutool.core.lang.Assert;
import com.schisandra.auth.common.entity.CaptchaResult;
import com.schisandra.auth.common.entity.Result;
import com.schisandra.auth.common.redis.RedisUtil;
import com.schisandra.auth.common.utils.AESUtils;
import com.schisandra.auth.common.utils.RotateImageUtils;
import com.schisandra.auth.common.utils.SmsCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@@ -11,97 +17,87 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.HashMap;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/ReactRotateCaptcha/")
@Slf4j
public class ReactRotateCaptchaController {
@PostMapping (value = "get", produces = MediaType.IMAGE_PNG_VALUE)
public ResponseEntity<String> get(){
//前端可以直接根据URL/image/{图片id} 来获取图片 注意:资源文件ID最好进行加密和设置有效期
// 在实际开发中 一般先通过图片id查看数据库有没有这条记录
String imageFilePath = "D:\\java_project\\schisandra-cloud-storage\\schisandra-cloud-storage-auth\\schisandra-cloud-storage-auth-application\\schisandra-cloud-storage-auth-application-controller\\src\\main\\java\\com\\schisandra\\auth\\application\\image\\test1.png";
File file = new File(imageFilePath);
try {
BufferedImage bufferedImage = ImageIO.read(file);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", byteArrayOutputStream);
byte[] imageBytes = byteArrayOutputStream.toByteArray(); // 读取图片数据的字节数组
// 将图片字节数组转换为Base64编码的字符串
String base64Image = Base64.getEncoder().encodeToString(imageBytes);
System.out.println(base64Image);
// 创建包含Base64编码的响应体的ResponseEntity对象并设置正确的媒体类型和内容长度
return ResponseEntity.ok().contentType(MediaType.IMAGE_PNG)
.body(base64Image);
}catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.ok().body(null);
}
@Resource
private RedisUtil redisUtil;
public final String authRotateCaptchaPrefix = "auth.RotateCaptcha";
/**
* 请求图片地址, 返回的结果进行base64编码
* @param imgUrl 图片地址
* @return
* @description:获取图片
* @param: []
* @return: java.lang.String
* @author zlg
* @date: 2024/5/10 16:28
*/
// public static String requestUrlToBase64(String imgUrl) {
// //读取图片字节数组
// byte[] data = null;
// try {
// InputStream in = new FileInputStream(imgUrl);
//// System.out.println("文件大小(字节)=" + in.available());
// data = new byte[in.available()];
// in.read(data);
// in.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
// //对字节数组进行Base64编码得到Base64编码的字符串
// return new String(Base64.encodeBase64(data));
//
// }
//
// public static File convertBase64ToFile(String fileBase64String, String filePath, String fileName) {
// BufferedOutputStream bos = null;
// FileOutputStream fos = null;
// File file = null;
// try {
// File dir = new File(filePath);
// //判断文件目录是否存在
// if (!dir.exists() && dir.isDirectory()) {
// dir.mkdirs();
// }
// byte[] bfile = Base64.decodeBase64(fileBase64String);
// file = new File(filePath + File.separator + fileName);
// fos = new FileOutputStream(file);
// bos = new BufferedOutputStream(fos);
// bos.write(bfile);
// return file;
// } catch (Exception e) {
// e.printStackTrace();
// } finally {
// if (bos != null) {
// try {
// bos.close();
// } catch (IOException e1) {
// e1.printStackTrace();
// }
// }
// if (fos != null) {
// try {
// fos.close();
// } catch (IOException e1) {
// e1.printStackTrace();
// }
// }
// }
// return null;
// }
@PostMapping("get")
public CaptchaResult get() throws Exception {
HashMap<String,String> map = new HashMap<>();
RotateImageUtils rotateImageUtils = new RotateImageUtils();
InputStream inputStream= ReactRotateCaptchaController.class.getClassLoader().getResourceAsStream("image/test1.jpg");
BufferedImage image = ImageIO.read(inputStream);
Random random = new Random();
double randomNumber = random.nextInt(280) + 40;
String key = AESUtils.getKey();
String token = AESUtils.encrypt(String.valueOf(randomNumber), key);
String prefix = redisUtil.buildKey(authRotateCaptchaPrefix, token);
redisUtil.setNx(prefix, String.valueOf(randomNumber), 60L, TimeUnit.SECONDS);
try {
BufferedImage image1 = rotateImageUtils.rotateImage(image, randomNumber, Color.black);
map.put("token",token);
map.put("str", RotateImageUtils.BufferedImageToBase64(image1));
return CaptchaResult.ok(map);
} catch (Exception e) {
return CaptchaResult.fail();
}
}
/**
* @description: 验证图片
* @param: [token, num]
* @return: com.schisandra.auth.common.entity.Result
* @author zlg
* @date: 2024/5/10 17:25
*/
@PostMapping("verfiy")
public CaptchaResult verfiy(String token, Double num) {
if (num == null && token == null) {
return CaptchaResult.fail();
}
String prefix = redisUtil.buildKey(authRotateCaptchaPrefix, token);
if (redisUtil.exist(prefix)) {
double realNum = Double.parseDouble(redisUtil.get(prefix));
if (Math.abs(realNum - num) / realNum < 0.05) {
redisUtil.del(prefix);
return CaptchaResult.ok();
} else {
return CaptchaResult.fail();
}
} else {
return CaptchaResult.fail();
}
}
}

View File

@@ -5,6 +5,7 @@ import cn.hutool.json.JSONUtil;
import com.xkcoding.justauth.AuthRequestFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;

View File

@@ -92,9 +92,10 @@
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-spring-boot-starter</artifactId>
<version>3.2.1</version>
<version> 3.2.1 </version>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-Email-core</artifactId>
@@ -108,12 +109,6 @@
<version>1.1.0</version>
</dependency>
<!-- lombok插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
@@ -122,12 +117,6 @@
<version>5.3.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
<dependency>
<groupId>org.apache.directory.studio</groupId>

View File

@@ -0,0 +1,46 @@
package com.schisandra.auth.common.entity;
import lombok.Data;
@Data
public class CaptchaResult<T> {
private Integer code;
private String msg;
private T data;
public static CaptchaResult ok() {
CaptchaResult result = new CaptchaResult();
result.setCode(0);
result.setMsg("Success");
return result;
}
public static <T> CaptchaResult ok(T data) {
CaptchaResult result = new CaptchaResult();
result.setCode(0);
result.setMsg("Success");
result.setData(data);
return result;
}
public static CaptchaResult fail() {
CaptchaResult result = new CaptchaResult();
result.setCode(1);
result.setMsg("Fail");
return result;
}
public static <T> CaptchaResult fail(T data) {
CaptchaResult result = new CaptchaResult();
result.setCode(1);
result.setMsg("Fail");
result.setData(data);
return result;
}
}

View File

@@ -0,0 +1,177 @@
package com.schisandra.auth.common.utils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
public class RotateImageUtils {
public static Color bgColor = new Color(255, 255, 255);
/**
* @description: 压缩图片并将图片转换成base64
* @param: [bufferedImage]
* @return: java.lang.String
* @author zlg
* @date: 2024/5/9 13:13
*/
public static String BufferedImageToBase64(BufferedImage bufferedImage) throws IOException {
int targetWidth = 400;
int targetHeight = 400;
// 获取原始图片的尺寸
int originalWidth = bufferedImage.getWidth();
int originalHeight = bufferedImage.getHeight();
// 创建一个新的 BufferedImage用于存放处理后的图片
BufferedImage outputImage = new BufferedImage(targetWidth, targetHeight, bufferedImage.getType());
// 创建一个 Graphics2D 对象,用于绘制新图片
Graphics2D graphics = outputImage.createGraphics();
// 如果原始图片尺寸大于目标尺寸,则进行压缩
if (originalWidth > targetWidth || originalHeight > targetHeight) {
Image scaledImage = bufferedImage.getScaledInstance(targetWidth, targetHeight, Image.SCALE_SMOOTH);
graphics.drawImage(scaledImage, 0, 0, null);
}
graphics.dispose();
ByteArrayOutputStream baos = new ByteArrayOutputStream();//io流
ImageIO.write(outputImage, "png", baos);//写入流中
byte[] bytes = baos.toByteArray();//转换成字节
BASE64Encoder encoder = new BASE64Encoder();
String png_base64 = encoder.encodeBuffer(bytes).trim();//转换成base64串
png_base64 = png_base64.replaceAll("\n", "").replaceAll("\r", "");//删除 \r\n
return "data:image/jpg;base64," + png_base64;
}
/**
* @description: 将base64转换成图片
* @param: [base64]
* @return: java.awt.image.BufferedImage
* @author zlg
* @date: 2024/5/9 13:14
*/
public static BufferedImage base64ToBufferedImage(String base64) {
BASE64Decoder decoder = new sun.misc.BASE64Decoder();
try {
byte[] bytes1 = decoder.decodeBuffer(base64);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes1);
return ImageIO.read(bais);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* @description: 获取旋转后的图片
* @param: [image, theta, backgroundColor]
* @return: java.awt.image.BufferedImage
* @author zlg
* @date: 2024/5/9 13:14
*/
public BufferedImage rotateImage(BufferedImage image, double theta, Color backgroundColor) {
int width = image.getWidth();
int height = image.getHeight();
double angle = theta * Math.PI / 180; // 度转弧度
double[] xCoords = getX(width / 2, height / 2, angle);
double[] yCoords = getY(width / 2, height / 2, angle);
int WIDTH = (int) (xCoords[3] - xCoords[0]);
int HEIGHT = (int) (yCoords[3] - yCoords[0]);
BufferedImage resultImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
int x = i - WIDTH / 2;
int y = HEIGHT / 2 - j;
double radius = Math.sqrt(x * x + y * y);
double angle1;
if (y > 0) {
angle1 = Math.acos(x / radius);
} else {
angle1 = 2 * Math.PI - Math.acos(x / radius);
}
x = (int) Math.round(radius * Math.cos(angle1 - angle));
y = (int) Math.round(radius * Math.sin(angle1 - angle));
if (x < (width / 2) & x > -(width / 2) & y < (height / 2) & y > -(height / 2)) {
int rgb = image.getRGB((int) Math.round(x + width / 2), (int) Math.round(height / 2 - y));
resultImage.setRGB(i, j, rgb);
} else {
resultImage.setRGB(i, j, -1);
}
}
}
return resultImage;
}
// 获取四个角点旋转后Y方向坐标
private double[] getY(int i, int j, double angle) {
double results[] = new double[4];
double radius = Math.sqrt(i * i + j * j);
double angle1 = Math.asin(j / radius);
results[0] = radius * Math.sin(angle1 + angle);
results[1] = radius * Math.sin(Math.PI - angle1 + angle);
results[2] = -results[0];
results[3] = -results[1];
Arrays.sort(results);
return results;
}
// 获取四个角点旋转后X方向坐标
private double[] getX(int i, int j, double angle) {
double results[] = new double[4];
double radius = Math.sqrt(i * i + j * j);
double angle1 = Math.acos(i / radius);
results[0] = radius * Math.cos(angle1 + angle);
results[1] = radius * Math.cos(Math.PI - angle1 + angle);
results[2] = -results[0];
results[3] = -results[1];
Arrays.sort(results);
return results;
}
/**
* @description: 获取旋转后的图片
* @param: [image]
* @return: java.awt.image.BufferedImage
* @author zlg
* @date: 2024/5/9 13:14
*/
public BufferedImage writeCyclePic(BufferedImage image) {
BufferedImage newImage = new BufferedImage(350, 350, BufferedImage.TYPE_INT_BGR);
try {
int width = image.getWidth();
int heigth = image.getHeight();
double x0 = width / 2;
double y0 = heigth / 2;
int woffset = (width - 350) / 2;
int hoffset = (heigth - 350) / 2;
for (int i = woffset; i < 350 + woffset; i++) {
for (int j = hoffset; j < 350 + hoffset; j++) {
double r = Math.sqrt(Math.pow(Math.abs(i - x0), 2.0) + Math.pow(Math.abs(j - y0), 2.0));
if (r > (x0 - woffset)) {
newImage.setRGB(i - woffset, j - hoffset, -1);
} else {
newImage.setRGB(i - woffset, j - hoffset, image.getRGB(i, j));
}
}
}
return newImage;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}

View File

@@ -37,7 +37,6 @@
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.schisandra</groupId>
<artifactId>schisandra-cloud-storage-auth-application-controller</artifactId>