验证码模块完善,增加配置项:
#验证码设置 verification-code: #图形验证码有效时间 秒 包含滑块验证码有效时间, 以及验证通过之后,缓存中存储的验证结果有效时间 effectiveTime: 300 #水印 watermark: LILI-SHOP #干扰项数量 最大2 默认0 interfereNum: 1 #允许误差像素 faultTolerant: 3
This commit is contained in:
parent
f9308fa5bf
commit
ef0eeda469
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"properties": [
|
|
||||||
{
|
|
||||||
"name": "spring.http.multipart.location",
|
|
||||||
"type": "java.lang.String",
|
|
||||||
"description": "Description for spring.http.multipart.location."
|
|
||||||
}
|
|
||||||
] }
|
|
@ -205,6 +205,16 @@ jasypt:
|
|||||||
password: lili
|
password: lili
|
||||||
|
|
||||||
lili:
|
lili:
|
||||||
|
#验证码设置
|
||||||
|
verification-code:
|
||||||
|
#图形验证码有效时间 秒 包含滑块验证码有效时间, 以及验证通过之后,缓存中存储的验证结果有效时间
|
||||||
|
effectiveTime: 300
|
||||||
|
#水印
|
||||||
|
watermark: LILI-SHOP
|
||||||
|
#干扰项数量 最大2 默认0
|
||||||
|
interfereNum: 0
|
||||||
|
#允许误差像素
|
||||||
|
faultTolerant: 3
|
||||||
#短信模版配置
|
#短信模版配置
|
||||||
sms:
|
sms:
|
||||||
#登录
|
#登录
|
||||||
|
@ -189,9 +189,9 @@ logging:
|
|||||||
# 输出级别
|
# 输出级别
|
||||||
level:
|
level:
|
||||||
cn.lili: info
|
cn.lili: info
|
||||||
# org.hibernate: debug
|
# org.hibernate: debug
|
||||||
# org.springframework: debug
|
# org.springframework: debug
|
||||||
# org.springframework.data.mongodb.core: debug
|
# org.springframework.data.mongodb.core: debug
|
||||||
file:
|
file:
|
||||||
# 指定路径
|
# 指定路径
|
||||||
path: lili-logs
|
path: lili-logs
|
||||||
@ -204,7 +204,18 @@ jasypt:
|
|||||||
encryptor:
|
encryptor:
|
||||||
password: lili
|
password: lili
|
||||||
|
|
||||||
|
|
||||||
lili:
|
lili:
|
||||||
|
#验证码设置
|
||||||
|
verification-code:
|
||||||
|
#图形验证码有效时间 秒 包含滑块验证码有效时间, 以及验证通过之后,缓存中存储的验证结果有效时间
|
||||||
|
effectiveTime: 300
|
||||||
|
#水印
|
||||||
|
watermark: LILI-SHOP
|
||||||
|
#干扰项数量 最大2 默认0
|
||||||
|
interfereNum: 1
|
||||||
|
#允许误差像素
|
||||||
|
faultTolerant: 3
|
||||||
#短信模版配置
|
#短信模版配置
|
||||||
sms:
|
sms:
|
||||||
#登录
|
#登录
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
package cn.lili.common.properties;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线程配置
|
||||||
|
*
|
||||||
|
* @author Chopper
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "lili.verification-code")
|
||||||
|
public class VerificationCodeProperties {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过期时间
|
||||||
|
* 包含滑块验证码有效时间, 以及验证通过之后,缓存中存储的验证结果有效时间
|
||||||
|
*/
|
||||||
|
private Long effectiveTime = 600L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 水印
|
||||||
|
*/
|
||||||
|
private String watermark = "LILI-SHOP";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 干扰数量 最大数量
|
||||||
|
*/
|
||||||
|
private Integer interfereNum = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 容错像素
|
||||||
|
*/
|
||||||
|
private Integer faultTolerant = 3;
|
||||||
|
|
||||||
|
|
||||||
|
public String getWatermark() {
|
||||||
|
return watermark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getInterfereNum() {
|
||||||
|
if (interfereNum > 2) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return interfereNum;
|
||||||
|
}
|
||||||
|
}
|
@ -330,7 +330,7 @@ public class CartServiceImpl implements CartService {
|
|||||||
double totalPrice = tradeDTO.getSkuList().stream().mapToDouble(i -> i.getPurchasePrice() * i.getNum()).sum();
|
double totalPrice = tradeDTO.getSkuList().stream().mapToDouble(i -> i.getPurchasePrice() * i.getNum()).sum();
|
||||||
if (tradeDTO.getSkuList() != null && !tradeDTO.getSkuList().isEmpty()) {
|
if (tradeDTO.getSkuList() != null && !tradeDTO.getSkuList().isEmpty()) {
|
||||||
List<String> ids = tradeDTO.getSkuList().parallelStream().filter(i -> Boolean.TRUE.equals(i.getChecked())).map(i -> i.getGoodsSku().getId()).collect(Collectors.toList());
|
List<String> ids = tradeDTO.getSkuList().parallelStream().filter(i -> Boolean.TRUE.equals(i.getChecked())).map(i -> i.getGoodsSku().getId()).collect(Collectors.toList());
|
||||||
List<String> storeIds = new ArrayList<>();
|
|
||||||
List<EsGoodsIndex> esGoodsList = esGoodsSearchService.getEsGoodsBySkuIds(ids);
|
List<EsGoodsIndex> esGoodsList = esGoodsSearchService.getEsGoodsBySkuIds(ids);
|
||||||
for (EsGoodsIndex esGoodsIndex : esGoodsList) {
|
for (EsGoodsIndex esGoodsIndex : esGoodsList) {
|
||||||
if (esGoodsIndex != null) {
|
if (esGoodsIndex != null) {
|
||||||
@ -341,9 +341,17 @@ public class CartServiceImpl implements CartService {
|
|||||||
count = currentGoodsCanUse.size();
|
count = currentGoodsCanUse.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
storeIds.add(esGoodsIndex.getStoreId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> storeIds = new ArrayList<>();
|
||||||
|
for (CartSkuVO cartSkuVO : tradeDTO.getSkuList()) {
|
||||||
|
if (!storeIds.contains(cartSkuVO.getStoreId())) {
|
||||||
|
storeIds.add(cartSkuVO.getStoreId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取可操作的优惠券集合
|
||||||
List<MemberCoupon> allScopeMemberCoupon = memberCouponService.getAllScopeMemberCoupon(tradeDTO.getMemberId(), storeIds);
|
List<MemberCoupon> allScopeMemberCoupon = memberCouponService.getAllScopeMemberCoupon(tradeDTO.getMemberId(), storeIds);
|
||||||
if (allScopeMemberCoupon != null && !allScopeMemberCoupon.isEmpty()) {
|
if (allScopeMemberCoupon != null && !allScopeMemberCoupon.isEmpty()) {
|
||||||
//过滤满足消费门槛
|
//过滤满足消费门槛
|
||||||
|
@ -35,6 +35,50 @@ public class ImageUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 干扰图
|
||||||
|
*
|
||||||
|
* @param oriImage 原图
|
||||||
|
* @param templateImage 模板图
|
||||||
|
* @param x 随机扣取坐标X
|
||||||
|
* @param y 随机扣取坐标y
|
||||||
|
*/
|
||||||
|
public static void interfereTemplate(BufferedImage oriImage, BufferedImage templateImage,
|
||||||
|
int x, int y) {
|
||||||
|
//临时数组遍历用于高斯模糊存周边像素值
|
||||||
|
int[][] matrix = new int[3][3];
|
||||||
|
int[] values = new int[9];
|
||||||
|
|
||||||
|
int xLength = templateImage.getWidth();
|
||||||
|
int yLength = templateImage.getHeight();
|
||||||
|
//模板图像宽度
|
||||||
|
for (int i = 0; i < xLength; i++) {
|
||||||
|
//模板图片高度
|
||||||
|
for (int j = 0; j < yLength; j++) {
|
||||||
|
//如果模板图像当前像素点不是透明色 copy源文件信息到目标图片中
|
||||||
|
int rgb = templateImage.getRGB(i, j);
|
||||||
|
if (rgb < 0) {
|
||||||
|
//抠图区域高斯模糊
|
||||||
|
readPixel(oriImage, x + i, y + j, values);
|
||||||
|
fillMatrix(matrix, values);
|
||||||
|
oriImage.setRGB(x + i, y + j, avgMatrix(matrix));
|
||||||
|
}
|
||||||
|
|
||||||
|
//防止数组越界判断
|
||||||
|
if (i == (xLength - 1) || j == (yLength - 1)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int rightRgb = templateImage.getRGB(i + 1, j);
|
||||||
|
int downRgb = templateImage.getRGB(i, j + 1);
|
||||||
|
//描边处理,,取带像素和无像素的界点,判断该点是不是临界轮廓点,如果是设置该坐标像素是白色
|
||||||
|
boolean rgbImage = ((rgb >= 0 && rightRgb < 0)
|
||||||
|
|| (rgb < 0 && rightRgb >= 0)
|
||||||
|
|| (rgb >= 0 && downRgb < 0)
|
||||||
|
|| (rgb < 0 && downRgb >= 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param oriImage 原图
|
* @param oriImage 原图
|
||||||
* @param templateImage 模板图
|
* @param templateImage 模板图
|
||||||
|
@ -3,7 +3,6 @@ package cn.lili.modules.verification;
|
|||||||
import cn.lili.common.utils.Base64DecodeMultipartFile;
|
import cn.lili.common.utils.Base64DecodeMultipartFile;
|
||||||
import cn.lili.common.vo.SerializableStream;
|
import cn.lili.common.vo.SerializableStream;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.util.Base64Utils;
|
import org.springframework.util.Base64Utils;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
@ -21,10 +20,10 @@ import java.util.Random;
|
|||||||
* @version v4.0
|
* @version v4.0
|
||||||
* @since 2020/11/17 14:34
|
* @since 2020/11/17 14:34
|
||||||
*/
|
*/
|
||||||
@Component
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class SliderImageUtil {
|
public class SliderImageUtil {
|
||||||
|
|
||||||
|
|
||||||
private static final int BOLD = 5;
|
private static final int BOLD = 5;
|
||||||
|
|
||||||
private static final String IMG_FILE_TYPE = "jpg";
|
private static final String IMG_FILE_TYPE = "jpg";
|
||||||
@ -34,12 +33,18 @@ public class SliderImageUtil {
|
|||||||
/**
|
/**
|
||||||
* 根据模板切图
|
* 根据模板切图
|
||||||
*
|
*
|
||||||
* @param sliderFile
|
* @param sliderFile 滑块
|
||||||
* @param originalFile
|
* @param originalFile 原图
|
||||||
* @return
|
* @param watermark 水印
|
||||||
|
* @param interfereNum 干扰选项
|
||||||
|
* @return 滑块参数
|
||||||
* @throws Exception sliderFile, originalFile
|
* @throws Exception sliderFile, originalFile
|
||||||
*/
|
*/
|
||||||
public static Map<String, Object> pictureTemplatesCut(SerializableStream sliderFile, SerializableStream originalFile) throws Exception {
|
public static Map<String, Object> pictureTemplatesCut(
|
||||||
|
SerializableStream sliderFile,
|
||||||
|
SerializableStream interfereSliderFile,
|
||||||
|
SerializableStream originalFile,
|
||||||
|
String watermark, Integer interfereNum) throws Exception {
|
||||||
|
|
||||||
Random random = new Random();
|
Random random = new Random();
|
||||||
Map<String, Object> pictureMap = new HashMap<>(16);
|
Map<String, Object> pictureMap = new HashMap<>(16);
|
||||||
@ -70,6 +75,18 @@ public class SliderImageUtil {
|
|||||||
//新建的图像根据模板颜色赋值,源图生成遮罩
|
//新建的图像根据模板颜色赋值,源图生成遮罩
|
||||||
ImageUtil.cutByTemplate(originalImage, sliderImage, newImage, randomX, randomY);
|
ImageUtil.cutByTemplate(originalImage, sliderImage, newImage, randomX, randomY);
|
||||||
|
|
||||||
|
|
||||||
|
//干扰项
|
||||||
|
if (interfereNum > 0) {
|
||||||
|
BufferedImage interfereSliderImage = ImageIO.read(Base64DecodeMultipartFile.base64ToInputStream(interfereSliderFile.getBase64()));
|
||||||
|
for (int i = 0; i < interfereNum; i++) {
|
||||||
|
int interfereX = random.nextInt(originalWidth - 3 * sliderWidth) + 2 * sliderWidth;
|
||||||
|
int interfereY = random.nextInt(originalHeight - sliderHeight);
|
||||||
|
ImageUtil.interfereTemplate(originalImage, interfereSliderImage, interfereX, interfereY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//设置“抗锯齿”的属性
|
//设置“抗锯齿”的属性
|
||||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
graphics.setStroke(new BasicStroke(BOLD, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
|
graphics.setStroke(new BasicStroke(BOLD, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
|
||||||
@ -77,7 +94,7 @@ public class SliderImageUtil {
|
|||||||
graphics.dispose();
|
graphics.dispose();
|
||||||
|
|
||||||
//添加水印
|
//添加水印
|
||||||
ImageUtil.addWatermark(originalImage, "LILI-SHOP");
|
ImageUtil.addWatermark(originalImage, watermark);
|
||||||
//新建流
|
//新建流
|
||||||
ByteArrayOutputStream newImageOs = new ByteArrayOutputStream();
|
ByteArrayOutputStream newImageOs = new ByteArrayOutputStream();
|
||||||
//利用ImageIO类提供的write方法,将bi以png图片的数据模式写入流。
|
//利用ImageIO类提供的write方法,将bi以png图片的数据模式写入流。
|
||||||
|
@ -4,14 +4,15 @@ import cn.lili.cache.Cache;
|
|||||||
import cn.lili.cache.CachePrefix;
|
import cn.lili.cache.CachePrefix;
|
||||||
import cn.lili.common.enums.ResultCode;
|
import cn.lili.common.enums.ResultCode;
|
||||||
import cn.lili.common.exception.ServiceException;
|
import cn.lili.common.exception.ServiceException;
|
||||||
|
import cn.lili.common.properties.VerificationCodeProperties;
|
||||||
import cn.lili.common.utils.StringUtils;
|
import cn.lili.common.utils.StringUtils;
|
||||||
import cn.lili.modules.verification.SliderImageUtil;
|
|
||||||
import cn.lili.modules.verification.enums.VerificationEnums;
|
|
||||||
import cn.lili.modules.verification.service.VerificationService;
|
|
||||||
import cn.lili.common.vo.SerializableStream;
|
import cn.lili.common.vo.SerializableStream;
|
||||||
import cn.lili.modules.system.entity.dos.VerificationSource;
|
import cn.lili.modules.system.entity.dos.VerificationSource;
|
||||||
import cn.lili.modules.system.entity.vo.VerificationDTO;
|
import cn.lili.modules.system.entity.vo.VerificationDTO;
|
||||||
import cn.lili.modules.system.service.VerificationSourceService;
|
import cn.lili.modules.system.service.VerificationSourceService;
|
||||||
|
import cn.lili.modules.verification.SliderImageUtil;
|
||||||
|
import cn.lili.modules.verification.enums.VerificationEnums;
|
||||||
|
import cn.lili.modules.verification.service.VerificationService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -36,6 +37,11 @@ public class VerificationServiceImpl implements VerificationService {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private VerificationSourceService verificationSourceService;
|
private VerificationSourceService verificationSourceService;
|
||||||
|
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private VerificationCodeProperties verificationCodeProperties;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Cache cache;
|
private Cache cache;
|
||||||
|
|
||||||
@ -68,22 +74,30 @@ public class VerificationServiceImpl implements VerificationService {
|
|||||||
String originalResource = verificationResources.get(resourceNum).getResource();
|
String originalResource = verificationResources.get(resourceNum).getResource();
|
||||||
//随机选择剪切模版图片地址
|
//随机选择剪切模版图片地址
|
||||||
String sliderResource = verificationSlider.get(sliderNum).getResource();
|
String sliderResource = verificationSlider.get(sliderNum).getResource();
|
||||||
|
// 干扰块
|
||||||
|
String interfereResource = verificationSlider.get(sliderNum == verificationSlider.size() - 1 ?
|
||||||
|
sliderNum - 1 : sliderNum + 1).getResource();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//获取缓存中的资源
|
//获取缓存中的资源
|
||||||
SerializableStream originalFile = getInputStream(originalResource);
|
SerializableStream originalFile = getInputStream(originalResource);
|
||||||
SerializableStream sliderFile = getInputStream(sliderResource);
|
SerializableStream sliderFile = getInputStream(sliderResource);
|
||||||
Map<String, Object> resultMap = SliderImageUtil.pictureTemplatesCut(sliderFile, originalFile);
|
SerializableStream interfereSliderFile = verificationCodeProperties.getInterfereNum() > 0 ? getInputStream(interfereResource) : null;
|
||||||
//生成验证参数 120可以验证 无需手动清除,120秒有效时间自动清除
|
//生成数据
|
||||||
cache.put(cacheKey(verificationEnums, uuid), resultMap.get("randomX"), 120L);
|
Map<String, Object> resultMap = SliderImageUtil.pictureTemplatesCut(
|
||||||
|
sliderFile, interfereSliderFile, originalFile,
|
||||||
|
verificationCodeProperties.getWatermark(), verificationCodeProperties.getInterfereNum());
|
||||||
|
//生成验证参数 有效时间 默认600秒,可以自行配置
|
||||||
|
cache.put(cacheKey(verificationEnums, uuid), resultMap.get("randomX"), verificationCodeProperties.getEffectiveTime());
|
||||||
resultMap.put("key", cacheKey(verificationEnums, uuid));
|
resultMap.put("key", cacheKey(verificationEnums, uuid));
|
||||||
|
resultMap.put("effectiveTime", verificationCodeProperties.getEffectiveTime());
|
||||||
//移除横坐标移动距离
|
//移除横坐标移动距离
|
||||||
resultMap.remove("randomX");
|
resultMap.remove("randomX");
|
||||||
return resultMap;
|
return resultMap;
|
||||||
} catch (ServiceException e) {
|
} catch (ServiceException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("创建校验错误",e);
|
log.error("创建校验错误", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,6 +105,7 @@ public class VerificationServiceImpl implements VerificationService {
|
|||||||
/**
|
/**
|
||||||
* 根据网络地址,获取源文件
|
* 根据网络地址,获取源文件
|
||||||
* 这里简单说一下,这里是将不可序列化的inputstream序列化对象,存入redis缓存
|
* 这里简单说一下,这里是将不可序列化的inputstream序列化对象,存入redis缓存
|
||||||
|
*
|
||||||
* @param originalResource
|
* @param originalResource
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ -125,11 +140,10 @@ public class VerificationServiceImpl implements VerificationService {
|
|||||||
}
|
}
|
||||||
log.debug("{}{}", randomX, xPos);
|
log.debug("{}{}", randomX, xPos);
|
||||||
//验证结果
|
//验证结果
|
||||||
boolean result = Math.abs(randomX - xPos) < 3;
|
if (Math.abs(randomX - xPos) < verificationCodeProperties.getFaultTolerant()) {
|
||||||
if (result) {
|
//验证成功,则记录验证结果 验证有效时间与验证码创建有效时间一致
|
||||||
//验证成功,则记录验证结果 验证有效时间,120秒
|
cache.put(cacheResult(verificationEnums, uuid), true, verificationCodeProperties.getEffectiveTime());
|
||||||
cache.put(cacheResult(verificationEnums, uuid), true, 120L);
|
return true;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user