修改命名
This commit is contained in:
parent
fdb2b98a98
commit
8eb80c7b65
@ -7,14 +7,14 @@
|
|||||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||||
<outputRelativeToContentRoot value="true" />
|
<outputRelativeToContentRoot value="true" />
|
||||||
<module name="YuNan-demo" />
|
<module name="YuNan-system-start" />
|
||||||
</profile>
|
</profile>
|
||||||
</annotationProcessing>
|
</annotationProcessing>
|
||||||
</component>
|
</component>
|
||||||
<component name="JavacSettings">
|
<component name="JavacSettings">
|
||||||
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
|
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
|
||||||
<module name="YuNan-cmd" options="" />
|
<module name="YuNan-cmd" options="" />
|
||||||
<module name="YuNan-demo" options="-parameters" />
|
<module name="YuNan-system-start" options="-parameters" />
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
|
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
|
||||||
<file url="file://$PROJECT_DIR$/YuNan-demo/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/YuNan-system-start/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/YuNan-demo/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/YuNan-system-start/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||||
<file url="PROJECT" charset="UTF-8" />
|
<file url="PROJECT" charset="UTF-8" />
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,7 +9,7 @@
|
|||||||
<version>1.0.0</version>
|
<version>1.0.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>YuNan-demo</artifactId>
|
<artifactId>YuNan-system-start</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
@ -1,5 +1,6 @@
|
|||||||
package com.yunan.config;
|
package com.yunan.config;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
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.JdkSerializationRedisSerializer;
|
||||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||||
|
|
||||||
|
|
||||||
/*告诉spring,运行的时候加载读取这个类*/
|
/*告诉spring,运行的时候加载读取这个类*/
|
||||||
|
@Slf4j
|
||||||
@Configuration
|
@Configuration
|
||||||
public class RedisConfig {
|
public class RedisConfig {
|
||||||
|
|
||||||
@ -20,6 +23,7 @@ public class RedisConfig {
|
|||||||
redisTemplate.setValueSerializer
|
redisTemplate.setValueSerializer
|
||||||
(new JdkSerializationRedisSerializer());
|
(new JdkSerializationRedisSerializer());
|
||||||
redisTemplate.setConnectionFactory(connectionFactory);
|
redisTemplate.setConnectionFactory(connectionFactory);
|
||||||
|
log.info("redis配置已加载!!!");
|
||||||
return redisTemplate;
|
return redisTemplate;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
56
YuNan-system-start/src/main/java/com/yunan/dto/Captcha.java
Normal file
56
YuNan-system-start/src/main/java/com/yunan/dto/Captcha.java
Normal file
@ -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;
|
||||||
|
}
|
@ -2,11 +2,13 @@ package com.yunan.dto;
|
|||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
import javax.validation.constraints.Email;
|
import javax.validation.constraints.Email;
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
@ApiModel("用户登入请求")
|
@ApiModel("用户登入请求")
|
||||||
|
@Data
|
||||||
public class LoginDTO {
|
public class LoginDTO {
|
||||||
|
|
||||||
@NotBlank(message = "用户名不能为空")
|
@NotBlank(message = "用户名不能为空")
|
@ -2,14 +2,16 @@ package com.yunan.dto;
|
|||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
import javax.validation.constraints.Email;
|
import javax.validation.constraints.Email;
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
@ApiModel("用户注册请求")
|
@ApiModel("用户注册请求")
|
||||||
|
@Data
|
||||||
public class RegisterDTO {
|
public class RegisterDTO {
|
||||||
@NotBlank(message = "用户名不能为空")
|
@NotBlank(message = "用户名不能为空")
|
||||||
@ApiModelProperty(value = "用户名", required = true)
|
@ApiModelProperty(value = "用户名")
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
@Email(message = "请输入有效的邮箱")
|
@Email(message = "请输入有效的邮箱")
|
||||||
@ -20,7 +22,7 @@ public class RegisterDTO {
|
|||||||
@ApiModelProperty(value = "密码", required = true)
|
@ApiModelProperty(value = "密码", required = true)
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
@ApiModelProperty(value = "验证码")
|
@ApiModelProperty(value = "验证码", required = true)
|
||||||
private String code;
|
private String code;
|
||||||
|
|
||||||
private String token;
|
private String token;
|
@ -1,4 +1,4 @@
|
|||||||
package com.yunan.pojo;
|
package com.yunan.dto;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ import java.io.Serializable;
|
|||||||
* 用户实体类
|
* 用户实体类
|
||||||
*/
|
*/
|
||||||
public class User implements Serializable {
|
public class User implements Serializable {
|
||||||
private int id;
|
private Integer id;
|
||||||
/** 用户名*/
|
/** 用户名*/
|
||||||
private String username;
|
private String username;
|
||||||
/** 密码*/
|
/** 密码*/
|
||||||
@ -32,7 +32,7 @@ public class User implements Serializable {
|
|||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId() {
|
public Integer getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
|||||||
package com.yunan.mapper;
|
package com.yunan.mapper;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.yunan.dto.RegisterDTO;
|
import com.yunan.dto.User;
|
||||||
import com.yunan.pojo.User;
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
70
YuNan-system-start/src/main/java/com/yunan/response/R.java
Normal file
70
YuNan-system-start/src/main/java/com/yunan/response/R.java
Normal file
@ -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 +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,6 @@ package com.yunan.service;
|
|||||||
|
|
||||||
|
|
||||||
import com.yunan.dto.RegisterDTO;
|
import com.yunan.dto.RegisterDTO;
|
||||||
import com.yunan.pojo.User;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
import javax.mail.MessagingException;
|
import javax.mail.MessagingException;
|
@ -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<String, String> 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<String, String> 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;
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@ package com.yunan.service.impl;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.yunan.dto.RegisterDTO;
|
import com.yunan.dto.RegisterDTO;
|
||||||
import com.yunan.mapper.AuthMapper;
|
import com.yunan.mapper.AuthMapper;
|
||||||
import com.yunan.pojo.User;
|
import com.yunan.dto.User;
|
||||||
import com.yunan.response.ErrorResponse;
|
import com.yunan.response.ErrorResponse;
|
||||||
import com.yunan.service.AuthService;
|
import com.yunan.service.AuthService;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -53,3 +53,6 @@ server:
|
|||||||
logging:
|
logging:
|
||||||
level:
|
level:
|
||||||
org.springframework.boot.context.web: DEBUG
|
org.springframework.boot.context.web: DEBUG
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
|||||||
package com.yunan;
|
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 {
|
class YuNanDemoApplicationTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
4
pom.xml
4
pom.xml
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<!-- 子模块列表 -->
|
<!-- 子模块列表 -->
|
||||||
<module>YuNan-demo</module>
|
<module>YuNan-system-start</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
@ -111,6 +111,8 @@
|
|||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-pool2</artifactId>
|
<artifactId>commons-pool2</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
Loading…
Reference in New Issue
Block a user