From 8eb80c7b6552e51c7c6bf34a727ef36440182816 Mon Sep 17 00:00:00 2001 From: YuNan <3194726156@qq.com> Date: Sat, 9 Nov 2024 13:22:03 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/compiler.xml | 4 +- .idea/encodings.xml | 4 +- .../com/yunan/config/GlobalCorsConfig.java | 30 --- {YuNan-demo => YuNan-system-start}/.gitignore | 0 .../.mvn/wrapper/maven-wrapper.properties | 0 {YuNan-demo => YuNan-system-start}/mvnw | 0 {YuNan-demo => YuNan-system-start}/mvnw.cmd | 0 {YuNan-demo => YuNan-system-start}/pom.xml | 2 +- .../java/com/yunan/YuNanDemoApplication.java | 0 .../java/com/yunan/config/RedisConfig.java | 4 + .../java/com/yunan/config/SwaggerConfig.java | 0 .../java/com/yunan/constant/ResponseCode.java | 0 .../com/yunan/controller/AuthController.java | 0 .../yunan/controller/CaptchaController.java | 33 +++ .../src/main/java/com/yunan/dto/Captcha.java | 56 +++++ .../src/main/java/com/yunan/dto/LoginDTO.java | 2 + .../main/java/com/yunan/dto/RegisterDTO.java | 6 +- .../src/main/java/com/yunan/dto}/User.java | 6 +- .../yunan/handler/GlobalExceptionHandler.java | 0 .../java/com/yunan/mapper/AuthMapper.java | 3 +- .../java/com/yunan/response/ApiResponse.java | 0 .../com/yunan/response/ErrorResponse.java | 0 .../src/main/java/com/yunan/response/R.java | 70 ++++++ .../java/com/yunan/service/AuthService.java | 1 - .../com/yunan/service/CaptchaService.java | 88 +++++++ .../java/com/yunan/service/EmailService.java | 0 .../yunan/service/impl/AuthServiceImpl.java | 2 +- .../java/com/yunan/util/CaptchaUtils.java | 229 ++++++++++++++++++ .../main/java/com/yunan/util/RedisUtils.java | 0 .../com/yunan/util/VerificationCodeUtils.java | 0 .../src/main/resources/application.yml | 3 + .../com/yunan/YuNanDemoApplicationTests.java | 6 +- pom.xml | 4 +- 33 files changed, 505 insertions(+), 48 deletions(-) delete mode 100644 YuNan-demo/src/main/java/com/yunan/config/GlobalCorsConfig.java rename {YuNan-demo => YuNan-system-start}/.gitignore (100%) rename {YuNan-demo => YuNan-system-start}/.mvn/wrapper/maven-wrapper.properties (100%) rename {YuNan-demo => YuNan-system-start}/mvnw (100%) rename {YuNan-demo => YuNan-system-start}/mvnw.cmd (100%) rename {YuNan-demo => YuNan-system-start}/pom.xml (94%) rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/YuNanDemoApplication.java (100%) rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/config/RedisConfig.java (91%) rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/config/SwaggerConfig.java (100%) rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/constant/ResponseCode.java (100%) rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/controller/AuthController.java (100%) create mode 100644 YuNan-system-start/src/main/java/com/yunan/controller/CaptchaController.java create mode 100644 YuNan-system-start/src/main/java/com/yunan/dto/Captcha.java rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/dto/LoginDTO.java (97%) rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/dto/RegisterDTO.java (91%) rename {YuNan-demo/src/main/java/com/yunan/pojo => YuNan-system-start/src/main/java/com/yunan/dto}/User.java (95%) rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/handler/GlobalExceptionHandler.java (100%) rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/mapper/AuthMapper.java (75%) rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/response/ApiResponse.java (100%) rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/response/ErrorResponse.java (100%) create mode 100644 YuNan-system-start/src/main/java/com/yunan/response/R.java rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/service/AuthService.java (93%) create mode 100644 YuNan-system-start/src/main/java/com/yunan/service/CaptchaService.java rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/service/EmailService.java (100%) rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/service/impl/AuthServiceImpl.java (98%) create mode 100644 YuNan-system-start/src/main/java/com/yunan/util/CaptchaUtils.java rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/util/RedisUtils.java (100%) rename {YuNan-demo => YuNan-system-start}/src/main/java/com/yunan/util/VerificationCodeUtils.java (100%) rename {YuNan-demo => YuNan-system-start}/src/main/resources/application.yml (99%) rename {YuNan-demo => YuNan-system-start}/src/test/java/com/yunan/YuNanDemoApplicationTests.java (76%) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 1064b9e..1393cd1 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -7,14 +7,14 @@ - + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml index e24ea69..a34fedc 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -1,8 +1,8 @@ - - + + diff --git a/YuNan-demo/src/main/java/com/yunan/config/GlobalCorsConfig.java b/YuNan-demo/src/main/java/com/yunan/config/GlobalCorsConfig.java deleted file mode 100644 index 0f44495..0000000 --- a/YuNan-demo/src/main/java/com/yunan/config/GlobalCorsConfig.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.yunan.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; - -@Configuration -public class GlobalCorsConfig { - /** - * 解决跨域问题 - * @return {@link CorsFilter } - */ - @Bean - public CorsFilter corsFilter() { - CorsConfiguration config = new CorsConfiguration(); - // 设置你要允许的网站域名 - config.addAllowedOrigin("http://localhost:3100"); - //允许跨域发送cookie - config.setAllowCredentials(true); - //放行全部原始头信息 - config.addAllowedHeader("*"); - //允许所有请求方法跨域调用 - config.addAllowedMethod("*"); - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", config); - return new CorsFilter(source); - } -} diff --git a/YuNan-demo/.gitignore b/YuNan-system-start/.gitignore similarity index 100% rename from YuNan-demo/.gitignore rename to YuNan-system-start/.gitignore diff --git a/YuNan-demo/.mvn/wrapper/maven-wrapper.properties b/YuNan-system-start/.mvn/wrapper/maven-wrapper.properties similarity index 100% rename from YuNan-demo/.mvn/wrapper/maven-wrapper.properties rename to YuNan-system-start/.mvn/wrapper/maven-wrapper.properties diff --git a/YuNan-demo/mvnw b/YuNan-system-start/mvnw similarity index 100% rename from YuNan-demo/mvnw rename to YuNan-system-start/mvnw diff --git a/YuNan-demo/mvnw.cmd b/YuNan-system-start/mvnw.cmd similarity index 100% rename from YuNan-demo/mvnw.cmd rename to YuNan-system-start/mvnw.cmd diff --git a/YuNan-demo/pom.xml b/YuNan-system-start/pom.xml similarity index 94% rename from YuNan-demo/pom.xml rename to YuNan-system-start/pom.xml index 659445e..2b599c9 100644 --- a/YuNan-demo/pom.xml +++ b/YuNan-system-start/pom.xml @@ -9,7 +9,7 @@ 1.0.0 - YuNan-demo + YuNan-system-start jar diff --git a/YuNan-demo/src/main/java/com/yunan/YuNanDemoApplication.java b/YuNan-system-start/src/main/java/com/yunan/YuNanDemoApplication.java similarity index 100% rename from YuNan-demo/src/main/java/com/yunan/YuNanDemoApplication.java rename to YuNan-system-start/src/main/java/com/yunan/YuNanDemoApplication.java diff --git a/YuNan-demo/src/main/java/com/yunan/config/RedisConfig.java b/YuNan-system-start/src/main/java/com/yunan/config/RedisConfig.java similarity index 91% rename from YuNan-demo/src/main/java/com/yunan/config/RedisConfig.java rename to YuNan-system-start/src/main/java/com/yunan/config/RedisConfig.java index fe12611..5cc3c82 100644 --- a/YuNan-demo/src/main/java/com/yunan/config/RedisConfig.java +++ b/YuNan-system-start/src/main/java/com/yunan/config/RedisConfig.java @@ -1,5 +1,6 @@ package com.yunan.config; +import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; @@ -7,7 +8,9 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; + /*告诉spring,运行的时候加载读取这个类*/ +@Slf4j @Configuration public class RedisConfig { @@ -20,6 +23,7 @@ public class RedisConfig { redisTemplate.setValueSerializer (new JdkSerializationRedisSerializer()); redisTemplate.setConnectionFactory(connectionFactory); + log.info("redis配置已加载!!!"); return redisTemplate; } } diff --git a/YuNan-demo/src/main/java/com/yunan/config/SwaggerConfig.java b/YuNan-system-start/src/main/java/com/yunan/config/SwaggerConfig.java similarity index 100% rename from YuNan-demo/src/main/java/com/yunan/config/SwaggerConfig.java rename to YuNan-system-start/src/main/java/com/yunan/config/SwaggerConfig.java diff --git a/YuNan-demo/src/main/java/com/yunan/constant/ResponseCode.java b/YuNan-system-start/src/main/java/com/yunan/constant/ResponseCode.java similarity index 100% rename from YuNan-demo/src/main/java/com/yunan/constant/ResponseCode.java rename to YuNan-system-start/src/main/java/com/yunan/constant/ResponseCode.java diff --git a/YuNan-demo/src/main/java/com/yunan/controller/AuthController.java b/YuNan-system-start/src/main/java/com/yunan/controller/AuthController.java similarity index 100% rename from YuNan-demo/src/main/java/com/yunan/controller/AuthController.java rename to YuNan-system-start/src/main/java/com/yunan/controller/AuthController.java diff --git a/YuNan-system-start/src/main/java/com/yunan/controller/CaptchaController.java b/YuNan-system-start/src/main/java/com/yunan/controller/CaptchaController.java new file mode 100644 index 0000000..aa340ee --- /dev/null +++ b/YuNan-system-start/src/main/java/com/yunan/controller/CaptchaController.java @@ -0,0 +1,33 @@ +package com.yunan.controller; + +import com.yunan.dto.Captcha; +import com.yunan.response.R; +import com.yunan.service.CaptchaService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + + + +@RestController +@RequestMapping("/captcha") +@Api(tags = "验证码拼图") +public class CaptchaController { + + @Autowired + private CaptchaService captchaService; + + @ApiOperation(value = "生成验证码拼图") + @PostMapping("get-captcha") + public R getCaptcha(@RequestBody Captcha captcha) { + return R.ok(captchaService.getCaptcha(captcha)); + } + +} + + diff --git a/YuNan-system-start/src/main/java/com/yunan/dto/Captcha.java b/YuNan-system-start/src/main/java/com/yunan/dto/Captcha.java new file mode 100644 index 0000000..fcc617f --- /dev/null +++ b/YuNan-system-start/src/main/java/com/yunan/dto/Captcha.java @@ -0,0 +1,56 @@ +package com.yunan.dto; + +import lombok.Data; + +@Data +public class Captcha { + + /** + * 随机字符串 + **/ + private String nonceStr; + /** + * 验证值 + **/ + private String value; + /** + * 生成的画布的base64 + **/ + private String canvasSrc; + /** + * 画布宽度 + **/ + private Integer canvasWidth; + /** + * 画布高度 + **/ + private Integer canvasHeight; + /** + * 生成的阻塞块的base64 + **/ + private String blockSrc; + /** + * 阻塞块宽度 + **/ + private Integer blockWidth; + /** + * 阻塞块高度 + **/ + private Integer blockHeight; + /** + * 阻塞块凸凹半径 + **/ + private Integer blockRadius; + /** + * 阻塞块的横轴坐标 + **/ + private Integer blockX; + /** + * 阻塞块的纵轴坐标 + **/ + private Integer blockY; + /** + * 图片获取位置 + **/ + private Integer place; +} diff --git a/YuNan-demo/src/main/java/com/yunan/dto/LoginDTO.java b/YuNan-system-start/src/main/java/com/yunan/dto/LoginDTO.java similarity index 97% rename from YuNan-demo/src/main/java/com/yunan/dto/LoginDTO.java rename to YuNan-system-start/src/main/java/com/yunan/dto/LoginDTO.java index 3ad94e1..66be2f6 100644 --- a/YuNan-demo/src/main/java/com/yunan/dto/LoginDTO.java +++ b/YuNan-system-start/src/main/java/com/yunan/dto/LoginDTO.java @@ -2,11 +2,13 @@ package com.yunan.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import lombok.Data; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; @ApiModel("用户登入请求") +@Data public class LoginDTO { @NotBlank(message = "用户名不能为空") diff --git a/YuNan-demo/src/main/java/com/yunan/dto/RegisterDTO.java b/YuNan-system-start/src/main/java/com/yunan/dto/RegisterDTO.java similarity index 91% rename from YuNan-demo/src/main/java/com/yunan/dto/RegisterDTO.java rename to YuNan-system-start/src/main/java/com/yunan/dto/RegisterDTO.java index b0a83f6..189ce2f 100644 --- a/YuNan-demo/src/main/java/com/yunan/dto/RegisterDTO.java +++ b/YuNan-system-start/src/main/java/com/yunan/dto/RegisterDTO.java @@ -2,14 +2,16 @@ package com.yunan.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import lombok.Data; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; @ApiModel("用户注册请求") +@Data public class RegisterDTO { @NotBlank(message = "用户名不能为空") - @ApiModelProperty(value = "用户名", required = true) + @ApiModelProperty(value = "用户名") private String username; @Email(message = "请输入有效的邮箱") @@ -20,7 +22,7 @@ public class RegisterDTO { @ApiModelProperty(value = "密码", required = true) private String password; - @ApiModelProperty(value = "验证码") + @ApiModelProperty(value = "验证码", required = true) private String code; private String token; diff --git a/YuNan-demo/src/main/java/com/yunan/pojo/User.java b/YuNan-system-start/src/main/java/com/yunan/dto/User.java similarity index 95% rename from YuNan-demo/src/main/java/com/yunan/pojo/User.java rename to YuNan-system-start/src/main/java/com/yunan/dto/User.java index c834d95..9e96c04 100644 --- a/YuNan-demo/src/main/java/com/yunan/pojo/User.java +++ b/YuNan-system-start/src/main/java/com/yunan/dto/User.java @@ -1,4 +1,4 @@ -package com.yunan.pojo; +package com.yunan.dto; import java.io.Serializable; @@ -6,7 +6,7 @@ import java.io.Serializable; * 用户实体类 */ public class User implements Serializable { - private int id; + private Integer id; /** 用户名*/ private String username; /** 密码*/ @@ -32,7 +32,7 @@ public class User implements Serializable { '}'; } - public int getId() { + public Integer getId() { return id; } diff --git a/YuNan-demo/src/main/java/com/yunan/handler/GlobalExceptionHandler.java b/YuNan-system-start/src/main/java/com/yunan/handler/GlobalExceptionHandler.java similarity index 100% rename from YuNan-demo/src/main/java/com/yunan/handler/GlobalExceptionHandler.java rename to YuNan-system-start/src/main/java/com/yunan/handler/GlobalExceptionHandler.java diff --git a/YuNan-demo/src/main/java/com/yunan/mapper/AuthMapper.java b/YuNan-system-start/src/main/java/com/yunan/mapper/AuthMapper.java similarity index 75% rename from YuNan-demo/src/main/java/com/yunan/mapper/AuthMapper.java rename to YuNan-system-start/src/main/java/com/yunan/mapper/AuthMapper.java index a3f9cfc..eecf0b7 100644 --- a/YuNan-demo/src/main/java/com/yunan/mapper/AuthMapper.java +++ b/YuNan-system-start/src/main/java/com/yunan/mapper/AuthMapper.java @@ -1,8 +1,7 @@ package com.yunan.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.yunan.dto.RegisterDTO; -import com.yunan.pojo.User; +import com.yunan.dto.User; import org.apache.ibatis.annotations.Mapper; @Mapper diff --git a/YuNan-demo/src/main/java/com/yunan/response/ApiResponse.java b/YuNan-system-start/src/main/java/com/yunan/response/ApiResponse.java similarity index 100% rename from YuNan-demo/src/main/java/com/yunan/response/ApiResponse.java rename to YuNan-system-start/src/main/java/com/yunan/response/ApiResponse.java diff --git a/YuNan-demo/src/main/java/com/yunan/response/ErrorResponse.java b/YuNan-system-start/src/main/java/com/yunan/response/ErrorResponse.java similarity index 100% rename from YuNan-demo/src/main/java/com/yunan/response/ErrorResponse.java rename to YuNan-system-start/src/main/java/com/yunan/response/ErrorResponse.java diff --git a/YuNan-system-start/src/main/java/com/yunan/response/R.java b/YuNan-system-start/src/main/java/com/yunan/response/R.java new file mode 100644 index 0000000..765b6b5 --- /dev/null +++ b/YuNan-system-start/src/main/java/com/yunan/response/R.java @@ -0,0 +1,70 @@ +package com.yunan.response; + +public class R { + + private int code; // 状态码,200代表成功,其他代表失败 + private String message; // 响应信息 + private Object data; // 响应数据 + + // 默认构造函数 + public R() { + } + + // 构造函数 + public R(int code, String message, Object data) { + this.code = code; + this.message = message; + this.data = data; + } + + // 成功返回 + public static R ok(Object data) { + return new R(200, "成功", data); // 200代表成功 + } + + // 错误返回 + public static R error(String message) { + return new R(500, message, null); // 500代表服务器错误,message为错误信息 + } + + // 错误返回,带code和message + public static R error(int code, String message) { + return new R(code, message, null); // 根据传入的code返回不同的错误 + } + + // get 和 set 方法 + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } + + // 重写toString方法,方便调试输出 + @Override + public String toString() { + return "R{" + + "code=" + code + + ", message='" + message + '\'' + + ", data=" + data + + '}'; + } +} + diff --git a/YuNan-demo/src/main/java/com/yunan/service/AuthService.java b/YuNan-system-start/src/main/java/com/yunan/service/AuthService.java similarity index 93% rename from YuNan-demo/src/main/java/com/yunan/service/AuthService.java rename to YuNan-system-start/src/main/java/com/yunan/service/AuthService.java index e293b2a..9f9b6b9 100644 --- a/YuNan-demo/src/main/java/com/yunan/service/AuthService.java +++ b/YuNan-system-start/src/main/java/com/yunan/service/AuthService.java @@ -2,7 +2,6 @@ package com.yunan.service; import com.yunan.dto.RegisterDTO; -import com.yunan.pojo.User; import org.springframework.http.ResponseEntity; import javax.mail.MessagingException; diff --git a/YuNan-system-start/src/main/java/com/yunan/service/CaptchaService.java b/YuNan-system-start/src/main/java/com/yunan/service/CaptchaService.java new file mode 100644 index 0000000..fc91503 --- /dev/null +++ b/YuNan-system-start/src/main/java/com/yunan/service/CaptchaService.java @@ -0,0 +1,88 @@ +package com.yunan.service; + +import com.github.xiaoymin.knife4j.core.util.StrUtil; +import com.yunan.dto.Captcha; +import com.yunan.util.CaptchaUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Service; + +import java.awt.image.BufferedImage; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +@Service +public class CaptchaService { + /** + * 拼图验证码允许偏差 + **/ + private static Integer ALLOW_DEVIATION = 3; + + @Autowired + private StringRedisTemplate stringRedisTemplate; + + /** + * 校验验证码 + * @param imageKey + * @param imageCode + * @return boolean + **/ + public String checkImageCode(String imageKey, String imageCode) { + ValueOperations ops = stringRedisTemplate.opsForValue(); + String text = ops.get("imageCode:" + imageKey); + if(StrUtil.isBlank(text)){ + return "验证码已失效"; + } + // 根据移动距离判断验证是否成功 + if (Math.abs(Integer.parseInt(text) - Integer.parseInt(imageCode)) > ALLOW_DEVIATION) { + return "验证失败,请控制拼图对齐缺口"; + } + return null; + } + /** + * 缓存验证码,有效期15分钟 + * @param key + * @param code + **/ + public void saveImageCode(String key, String code) { + ValueOperations ops = stringRedisTemplate.opsForValue(); + ops.set("imageCode:" + key, code, 15, TimeUnit.MINUTES); + } + + /** + * 获取验证码拼图(生成的抠图和带抠图阴影的大图及抠图坐标) + **/ + public Object getCaptcha(Captcha captcha) { + //参数校验 + CaptchaUtils.checkCaptcha(captcha); + //获取画布的宽高 + int canvasWidth = captcha.getCanvasWidth(); + int canvasHeight = captcha.getCanvasHeight(); + //获取阻塞块的宽高/半径 + int blockWidth = captcha.getBlockWidth(); + int blockHeight = captcha.getBlockHeight(); + int blockRadius = captcha.getBlockRadius(); + //获取资源图 + BufferedImage canvasImage = CaptchaUtils.getBufferedImage(captcha.getPlace()); + //调整原图到指定大小 + canvasImage = CaptchaUtils.imageResize(canvasImage, canvasWidth, canvasHeight); + //随机生成阻塞块坐标 + int blockX = CaptchaUtils.getNonceByRange(blockWidth, canvasWidth - blockWidth - 10); + int blockY = CaptchaUtils.getNonceByRange(10, canvasHeight - blockHeight + 1); + //阻塞块 + BufferedImage blockImage = new BufferedImage(blockWidth, blockHeight, BufferedImage.TYPE_4BYTE_ABGR); + //新建的图像根据轮廓图颜色赋值,源图生成遮罩 + CaptchaUtils.cutByTemplate(canvasImage, blockImage, blockWidth, blockHeight, blockRadius, blockX, blockY); + // 移动横坐标 + String nonceStr = UUID.randomUUID().toString().replaceAll("-", ""); + // 缓存 + saveImageCode(nonceStr,String.valueOf(blockX)); + //设置返回参数 + captcha.setNonceStr(nonceStr); + captcha.setBlockY(blockY); + captcha.setBlockSrc(CaptchaUtils.toBase64(blockImage, "png")); + captcha.setCanvasSrc(CaptchaUtils.toBase64(canvasImage, "png")); + return captcha; + } +} diff --git a/YuNan-demo/src/main/java/com/yunan/service/EmailService.java b/YuNan-system-start/src/main/java/com/yunan/service/EmailService.java similarity index 100% rename from YuNan-demo/src/main/java/com/yunan/service/EmailService.java rename to YuNan-system-start/src/main/java/com/yunan/service/EmailService.java diff --git a/YuNan-demo/src/main/java/com/yunan/service/impl/AuthServiceImpl.java b/YuNan-system-start/src/main/java/com/yunan/service/impl/AuthServiceImpl.java similarity index 98% rename from YuNan-demo/src/main/java/com/yunan/service/impl/AuthServiceImpl.java rename to YuNan-system-start/src/main/java/com/yunan/service/impl/AuthServiceImpl.java index 8012859..eef98fd 100644 --- a/YuNan-demo/src/main/java/com/yunan/service/impl/AuthServiceImpl.java +++ b/YuNan-system-start/src/main/java/com/yunan/service/impl/AuthServiceImpl.java @@ -3,7 +3,7 @@ package com.yunan.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.yunan.dto.RegisterDTO; import com.yunan.mapper.AuthMapper; -import com.yunan.pojo.User; +import com.yunan.dto.User; import com.yunan.response.ErrorResponse; import com.yunan.service.AuthService; import org.springframework.http.HttpStatus; diff --git a/YuNan-system-start/src/main/java/com/yunan/util/CaptchaUtils.java b/YuNan-system-start/src/main/java/com/yunan/util/CaptchaUtils.java new file mode 100644 index 0000000..705392c --- /dev/null +++ b/YuNan-system-start/src/main/java/com/yunan/util/CaptchaUtils.java @@ -0,0 +1,229 @@ +package com.yunan.util; + +import com.yunan.dto.Captcha; +import org.apache.commons.lang3.RandomUtils; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.Base64; +import java.util.Objects; +import java.util.Random; + +public class CaptchaUtils { + + /** + * 网络图片地址 + **/ + private final static String IMG_URL = "https://loyer.wang/view/ftp/wallpaper/%s.jpg"; + + /** + * 本地图片地址 + **/ + private final static String IMG_PATH = "C:/Users/NanYu/Pictures/ComputerPaper/%s.jpg"; + + /** + * 入参校验设置默认值 + **/ + public static void checkCaptcha(Captcha captcha) { + //设置画布宽度默认值 + if (captcha.getCanvasWidth() == null) { + captcha.setCanvasWidth(320); + } + //设置画布高度默认值 + if (captcha.getCanvasHeight() == null) { + captcha.setCanvasHeight(155); + } + //设置阻塞块宽度默认值 + if (captcha.getBlockWidth() == null) { + captcha.setBlockWidth(65); + } + //设置阻塞块高度默认值 + if (captcha.getBlockHeight() == null) { + captcha.setBlockHeight(55); + } + //设置阻塞块凹凸半径默认值 + if (captcha.getBlockRadius() == null) { + captcha.setBlockRadius(9); + } + //设置图片来源默认值 + if (captcha.getPlace() == null) { + captcha.setPlace(0); + } + } + + /** + * 获取指定范围内的随机数 + **/ + public static int getNonceByRange(int start, int end) { + Random random = new Random(); + return random.nextInt(end - start + 1) + start; + } + + /** + * 获取验证码资源图 + **/ + public static BufferedImage getBufferedImage(Integer place) { + try { + //随机图片 +// int nonce = getNonceByRange(0, 1000); + int nonce = 111; + + //获取网络资源图片 + if (0 == place) { + String imgUrl = String.format(IMG_URL, nonce); + URL url = new URL(imgUrl); + return ImageIO.read(url.openStream()); + } + //获取本地图片 + else { + String imgPath = String.format(IMG_PATH, nonce); + File file = new File(imgPath); + return ImageIO.read(file); + } + } catch (Exception e) { + System.out.println("获取拼图资源失败"); + //异常处理 + return null; + } + } + + /** + * 调整图片大小 + **/ + public static BufferedImage imageResize(BufferedImage bufferedImage, int width, int height) { + Image image = bufferedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH); + BufferedImage resultImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics2D = resultImage.createGraphics(); + graphics2D.drawImage(image, 0, 0, null); + graphics2D.dispose(); + return resultImage; + } + + /** + * 抠图,并生成阻塞块 + **/ + public static void cutByTemplate(BufferedImage canvasImage, BufferedImage blockImage, int blockWidth, int blockHeight, int blockRadius, int blockX, int blockY) { + BufferedImage waterImage = new BufferedImage(blockWidth, blockHeight, BufferedImage.TYPE_4BYTE_ABGR); + //阻塞块的轮廓图 + int[][] blockData = getBlockData(blockWidth, blockHeight, blockRadius); + //创建阻塞块具体形状 + for (int i = 0; i < blockWidth; i++) { + for (int j = 0; j < blockHeight; j++) { + try { + //原图中对应位置变色处理 + if (blockData[i][j] == 1) { + //背景设置为黑色 + waterImage.setRGB(i, j, Color.BLACK.getRGB()); + blockImage.setRGB(i, j, canvasImage.getRGB(blockX + i, blockY + j)); + //轮廓设置为白色,取带像素和无像素的界点,判断该点是不是临界轮廓点 + if (blockData[i + 1][j] == 0 || blockData[i][j + 1] == 0 || blockData[i - 1][j] == 0 || blockData[i][j - 1] == 0) { + blockImage.setRGB(i, j, Color.WHITE.getRGB()); + waterImage.setRGB(i, j, Color.WHITE.getRGB()); + } + } + //这里把背景设为透明 + else { + blockImage.setRGB(i, j, Color.TRANSLUCENT); + waterImage.setRGB(i, j, Color.TRANSLUCENT); + } + } catch (ArrayIndexOutOfBoundsException e) { + //防止数组下标越界异常 + } + } + } + //在画布上添加阻塞块水印 + addBlockWatermark(canvasImage, waterImage, blockX, blockY); + } + + /** + * 构建拼图轮廓轨迹 + **/ + private static int[][] getBlockData(int blockWidth, int blockHeight, int blockRadius) { + int[][] data = new int[blockWidth][blockHeight]; + double po = Math.pow(blockRadius, 2); + //随机生成两个圆的坐标,在4个方向上 随机找到2个方向添加凸/凹 + //凸/凹1 + int face1 = RandomUtils.nextInt(0,4); + //凸/凹2 + int face2; + //保证两个凸/凹不在同一位置 + do { + face2 = RandomUtils.nextInt(0,4); + } while (face1 == face2); + //获取凸/凹起位置坐标 + int[] circle1 = getCircleCoords(face1, blockWidth, blockHeight, blockRadius); + int[] circle2 = getCircleCoords(face2, blockWidth, blockHeight, blockRadius); + //随机凸/凹类型 + int shape = getNonceByRange(0, 1); + //圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆 + //计算需要的小图轮廓,用二维数组来表示,二维数组有两张值,0和1,其中0表示没有颜色,1有颜色 + for (int i = 0; i < blockWidth; i++) { + for (int j = 0; j < blockHeight; j++) { + data[i][j] = 0; + //创建中间的方形区域 + if ((i >= blockRadius && i <= blockWidth - blockRadius && j >= blockRadius && j <= blockHeight - blockRadius)) { + data[i][j] = 1; + } + double d1 = Math.pow(i - Objects.requireNonNull(circle1)[0], 2) + Math.pow(j - circle1[1], 2); + double d2 = Math.pow(i - Objects.requireNonNull(circle2)[0], 2) + Math.pow(j - circle2[1], 2); + //创建两个凸/凹 + if (d1 <= po || d2 <= po) { + data[i][j] = shape; + } + } + } + return data; + } + /** + * 根据朝向获取圆心坐标 + */ + private static int[] getCircleCoords(int face, int blockWidth, int blockHeight, int blockRadius) { + //上 + if (0 == face) { + return new int[]{blockWidth / 2 - 1, blockRadius}; + } + //左 + else if (1 == face) { + return new int[]{blockRadius, blockHeight / 2 - 1}; + } + //下 + else if (2 == face) { + return new int[]{blockWidth / 2 - 1, blockHeight - blockRadius - 1}; + } + //右 + else if (3 == face) { + return new int[]{blockWidth - blockRadius - 1, blockHeight / 2 - 1}; + } + return null; + } + /** + * 在画布上添加阻塞块水印 + */ + private static void addBlockWatermark(BufferedImage canvasImage, BufferedImage blockImage, int x, int y) { + Graphics2D graphics2D = canvasImage.createGraphics(); + graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.8f)); + graphics2D.drawImage(blockImage, x, y, null); + graphics2D.dispose(); + } + /** + * BufferedImage转BASE64 + */ + public static String toBase64(BufferedImage bufferedImage, String type) { + try { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ImageIO.write(bufferedImage, type, byteArrayOutputStream); + String base64 = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()); + return String.format("data:image/%s;base64,%s", type, base64); + } catch (IOException e) { + System.out.println("图片资源转换BASE64失败"); + //异常处理 + return null; + } + } +} + diff --git a/YuNan-demo/src/main/java/com/yunan/util/RedisUtils.java b/YuNan-system-start/src/main/java/com/yunan/util/RedisUtils.java similarity index 100% rename from YuNan-demo/src/main/java/com/yunan/util/RedisUtils.java rename to YuNan-system-start/src/main/java/com/yunan/util/RedisUtils.java diff --git a/YuNan-demo/src/main/java/com/yunan/util/VerificationCodeUtils.java b/YuNan-system-start/src/main/java/com/yunan/util/VerificationCodeUtils.java similarity index 100% rename from YuNan-demo/src/main/java/com/yunan/util/VerificationCodeUtils.java rename to YuNan-system-start/src/main/java/com/yunan/util/VerificationCodeUtils.java diff --git a/YuNan-demo/src/main/resources/application.yml b/YuNan-system-start/src/main/resources/application.yml similarity index 99% rename from YuNan-demo/src/main/resources/application.yml rename to YuNan-system-start/src/main/resources/application.yml index 62ba5d0..51c86a0 100644 --- a/YuNan-demo/src/main/resources/application.yml +++ b/YuNan-system-start/src/main/resources/application.yml @@ -53,3 +53,6 @@ server: logging: level: org.springframework.boot.context.web: DEBUG + + + diff --git a/YuNan-demo/src/test/java/com/yunan/YuNanDemoApplicationTests.java b/YuNan-system-start/src/test/java/com/yunan/YuNanDemoApplicationTests.java similarity index 76% rename from YuNan-demo/src/test/java/com/yunan/YuNanDemoApplicationTests.java rename to YuNan-system-start/src/test/java/com/yunan/YuNanDemoApplicationTests.java index 6c6b8d7..1a06e2a 100644 --- a/YuNan-demo/src/test/java/com/yunan/YuNanDemoApplicationTests.java +++ b/YuNan-system-start/src/test/java/com/yunan/YuNanDemoApplicationTests.java @@ -1,9 +1,9 @@ package com.yunan; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -@SpringBootTest +import org.springframework.boot.test.context.SpringBootTest; +import org.testng.annotations.Test; + class YuNanDemoApplicationTests { @Test diff --git a/pom.xml b/pom.xml index fd225f0..f15d0e1 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ - YuNan-demo + YuNan-system-start @@ -111,6 +111,8 @@ org.apache.commons commons-pool2 + +