diff --git a/common-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/common-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json deleted file mode 100644 index 62b698e0..00000000 --- a/common-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "properties": [ - { - "name": "spring.http.multipart.location", - "type": "java.lang.String", - "description": "Description for spring.http.multipart.location." - } -] } \ No newline at end of file diff --git a/common-api/src/main/resources/application.yml b/common-api/src/main/resources/application.yml index fb067c71..dea7746a 100644 --- a/common-api/src/main/resources/application.yml +++ b/common-api/src/main/resources/application.yml @@ -205,6 +205,16 @@ jasypt: password: lili lili: + #验证码设置 + verification-code: + #图形验证码有效时间 秒 包含滑块验证码有效时间, 以及验证通过之后,缓存中存储的验证结果有效时间 + effectiveTime: 300 + #水印 + watermark: LILI-SHOP + #干扰项数量 最大2 默认0 + interfereNum: 0 + #允许误差像素 + faultTolerant: 3 #短信模版配置 sms: #登录 diff --git a/config/application.yml b/config/application.yml index f3d8b6bb..7f0fc0b9 100644 --- a/config/application.yml +++ b/config/application.yml @@ -189,9 +189,9 @@ logging: # 输出级别 level: cn.lili: info -# org.hibernate: debug -# org.springframework: debug -# org.springframework.data.mongodb.core: debug + # org.hibernate: debug + # org.springframework: debug + # org.springframework.data.mongodb.core: debug file: # 指定路径 path: lili-logs @@ -204,7 +204,18 @@ jasypt: encryptor: password: lili + lili: + #验证码设置 + verification-code: + #图形验证码有效时间 秒 包含滑块验证码有效时间, 以及验证通过之后,缓存中存储的验证结果有效时间 + effectiveTime: 300 + #水印 + watermark: LILI-SHOP + #干扰项数量 最大2 默认0 + interfereNum: 1 + #允许误差像素 + faultTolerant: 3 #短信模版配置 sms: #登录 diff --git a/framework/src/main/java/cn/lili/common/properties/VerificationCodeProperties.java b/framework/src/main/java/cn/lili/common/properties/VerificationCodeProperties.java new file mode 100644 index 00000000..c61b1545 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/properties/VerificationCodeProperties.java @@ -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; + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/service/CartServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/cart/service/CartServiceImpl.java index b60a5947..023bcfa4 100644 --- a/framework/src/main/java/cn/lili/modules/order/cart/service/CartServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/order/cart/service/CartServiceImpl.java @@ -330,7 +330,7 @@ public class CartServiceImpl implements CartService { double totalPrice = tradeDTO.getSkuList().stream().mapToDouble(i -> i.getPurchasePrice() * i.getNum()).sum(); if (tradeDTO.getSkuList() != null && !tradeDTO.getSkuList().isEmpty()) { List ids = tradeDTO.getSkuList().parallelStream().filter(i -> Boolean.TRUE.equals(i.getChecked())).map(i -> i.getGoodsSku().getId()).collect(Collectors.toList()); - List storeIds = new ArrayList<>(); + List esGoodsList = esGoodsSearchService.getEsGoodsBySkuIds(ids); for (EsGoodsIndex esGoodsIndex : esGoodsList) { if (esGoodsIndex != null) { @@ -341,9 +341,17 @@ public class CartServiceImpl implements CartService { count = currentGoodsCanUse.size(); } } - storeIds.add(esGoodsIndex.getStoreId()); } } + + List storeIds = new ArrayList<>(); + for (CartSkuVO cartSkuVO : tradeDTO.getSkuList()) { + if (!storeIds.contains(cartSkuVO.getStoreId())) { + storeIds.add(cartSkuVO.getStoreId()); + } + } + + //获取可操作的优惠券集合 List allScopeMemberCoupon = memberCouponService.getAllScopeMemberCoupon(tradeDTO.getMemberId(), storeIds); if (allScopeMemberCoupon != null && !allScopeMemberCoupon.isEmpty()) { //过滤满足消费门槛 diff --git a/framework/src/main/java/cn/lili/modules/verification/ImageUtil.java b/framework/src/main/java/cn/lili/modules/verification/ImageUtil.java index 98805297..71792fa3 100644 --- a/framework/src/main/java/cn/lili/modules/verification/ImageUtil.java +++ b/framework/src/main/java/cn/lili/modules/verification/ImageUtil.java @@ -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 templateImage 模板图 diff --git a/framework/src/main/java/cn/lili/modules/verification/SliderImageUtil.java b/framework/src/main/java/cn/lili/modules/verification/SliderImageUtil.java index 90ca90a3..30de8a4a 100644 --- a/framework/src/main/java/cn/lili/modules/verification/SliderImageUtil.java +++ b/framework/src/main/java/cn/lili/modules/verification/SliderImageUtil.java @@ -3,7 +3,6 @@ package cn.lili.modules.verification; import cn.lili.common.utils.Base64DecodeMultipartFile; import cn.lili.common.vo.SerializableStream; import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; import org.springframework.util.Base64Utils; import javax.imageio.ImageIO; @@ -21,10 +20,10 @@ import java.util.Random; * @version v4.0 * @since 2020/11/17 14:34 */ -@Component @Slf4j public class SliderImageUtil { + private static final int BOLD = 5; private static final String IMG_FILE_TYPE = "jpg"; @@ -34,12 +33,18 @@ public class SliderImageUtil { /** * 根据模板切图 * - * @param sliderFile - * @param originalFile - * @return + * @param sliderFile 滑块 + * @param originalFile 原图 + * @param watermark 水印 + * @param interfereNum 干扰选项 + * @return 滑块参数 * @throws Exception sliderFile, originalFile */ - public static Map pictureTemplatesCut(SerializableStream sliderFile, SerializableStream originalFile) throws Exception { + public static Map pictureTemplatesCut( + SerializableStream sliderFile, + SerializableStream interfereSliderFile, + SerializableStream originalFile, + String watermark, Integer interfereNum) throws Exception { Random random = new Random(); Map pictureMap = new HashMap<>(16); @@ -70,6 +75,18 @@ public class SliderImageUtil { //新建的图像根据模板颜色赋值,源图生成遮罩 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.setStroke(new BasicStroke(BOLD, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); @@ -77,7 +94,7 @@ public class SliderImageUtil { graphics.dispose(); //添加水印 - ImageUtil.addWatermark(originalImage, "LILI-SHOP"); + ImageUtil.addWatermark(originalImage, watermark); //新建流 ByteArrayOutputStream newImageOs = new ByteArrayOutputStream(); //利用ImageIO类提供的write方法,将bi以png图片的数据模式写入流。 diff --git a/framework/src/main/java/cn/lili/modules/verification/service/impl/VerificationServiceImpl.java b/framework/src/main/java/cn/lili/modules/verification/service/impl/VerificationServiceImpl.java index bfc617cf..9f35be8c 100644 --- a/framework/src/main/java/cn/lili/modules/verification/service/impl/VerificationServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/verification/service/impl/VerificationServiceImpl.java @@ -4,14 +4,15 @@ import cn.lili.cache.Cache; import cn.lili.cache.CachePrefix; import cn.lili.common.enums.ResultCode; import cn.lili.common.exception.ServiceException; +import cn.lili.common.properties.VerificationCodeProperties; 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.modules.system.entity.dos.VerificationSource; import cn.lili.modules.system.entity.vo.VerificationDTO; 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 org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -36,6 +37,11 @@ public class VerificationServiceImpl implements VerificationService { @Autowired private VerificationSourceService verificationSourceService; + + + @Autowired + private VerificationCodeProperties verificationCodeProperties; + @Autowired private Cache cache; @@ -68,22 +74,30 @@ public class VerificationServiceImpl implements VerificationService { String originalResource = verificationResources.get(resourceNum).getResource(); //随机选择剪切模版图片地址 String sliderResource = verificationSlider.get(sliderNum).getResource(); + // 干扰块 + String interfereResource = verificationSlider.get(sliderNum == verificationSlider.size() - 1 ? + sliderNum - 1 : sliderNum + 1).getResource(); try { //获取缓存中的资源 SerializableStream originalFile = getInputStream(originalResource); SerializableStream sliderFile = getInputStream(sliderResource); - Map resultMap = SliderImageUtil.pictureTemplatesCut(sliderFile, originalFile); - //生成验证参数 120可以验证 无需手动清除,120秒有效时间自动清除 - cache.put(cacheKey(verificationEnums, uuid), resultMap.get("randomX"), 120L); + SerializableStream interfereSliderFile = verificationCodeProperties.getInterfereNum() > 0 ? getInputStream(interfereResource) : null; + //生成数据 + Map 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("effectiveTime", verificationCodeProperties.getEffectiveTime()); //移除横坐标移动距离 resultMap.remove("randomX"); return resultMap; } catch (ServiceException e) { throw e; } catch (Exception e) { - log.error("创建校验错误",e); + log.error("创建校验错误", e); return null; } } @@ -91,6 +105,7 @@ public class VerificationServiceImpl implements VerificationService { /** * 根据网络地址,获取源文件 * 这里简单说一下,这里是将不可序列化的inputstream序列化对象,存入redis缓存 + * * @param originalResource * @return */ @@ -125,11 +140,10 @@ public class VerificationServiceImpl implements VerificationService { } log.debug("{}{}", randomX, xPos); //验证结果 - boolean result = Math.abs(randomX - xPos) < 3; - if (result) { - //验证成功,则记录验证结果 验证有效时间,120秒 - cache.put(cacheResult(verificationEnums, uuid), true, 120L); - return result; + if (Math.abs(randomX - xPos) < verificationCodeProperties.getFaultTolerant()) { + //验证成功,则记录验证结果 验证有效时间与验证码创建有效时间一致 + cache.put(cacheResult(verificationEnums, uuid), true, verificationCodeProperties.getEffectiveTime()); + return true; } return false; }