From 9ec129809bc3a2fffca1bc08a99b430000db0169 Mon Sep 17 00:00:00 2001 From: fengshuonan Date: Mon, 5 Jul 2021 15:45:30 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=907.0.4=E3=80=91=E3=80=90captcha?= =?UTF-8?q?=E3=80=91=E6=95=B4=E7=90=86=E9=AA=8C=E8=AF=81=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kernel/rule/constants/RuleConstants.java | 5 + .../kernel/auth/auth/AuthServiceImpl.java | 4 +- .../kernel/security/api/DragCaptchaApi.java | 53 ++++ .../{CaptchaApi.java => ImageCaptchaApi.java} | 6 +- .../enums/SecurityExceptionEnum.java | 4 +- .../api/pojo/DragCaptchaImageDTO.java | 48 ++++ .../{EasyCaptcha.java => ImageCaptcha.java} | 2 +- .../security/captcha/DragCaptchaService.java | 97 +++++++ ...aService.java => ImageCaptchaService.java} | 12 +- .../captcha/util/DragCaptchaImageUtil.java | 261 ++++++++++++++++++ .../starter/CaptchaAutoConfiguration.java | 27 +- .../service/impl/SysSmsInfoServiceImpl.java | 4 +- .../user/controller/KaptchaController.java | 35 ++- 13 files changed, 533 insertions(+), 25 deletions(-) create mode 100644 kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/DragCaptchaApi.java rename kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/{CaptchaApi.java => ImageCaptchaApi.java} (93%) create mode 100644 kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/pojo/DragCaptchaImageDTO.java rename kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/pojo/{EasyCaptcha.java => ImageCaptcha.java} (98%) create mode 100644 kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/DragCaptchaService.java rename kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/{CaptchaService.java => ImageCaptchaService.java} (85%) create mode 100644 kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/util/DragCaptchaImageUtil.java diff --git a/kernel-a-rule/src/main/java/cn/stylefeng/roses/kernel/rule/constants/RuleConstants.java b/kernel-a-rule/src/main/java/cn/stylefeng/roses/kernel/rule/constants/RuleConstants.java index b1bc8b880..e680fa3f9 100644 --- a/kernel-a-rule/src/main/java/cn/stylefeng/roses/kernel/rule/constants/RuleConstants.java +++ b/kernel-a-rule/src/main/java/cn/stylefeng/roses/kernel/rule/constants/RuleConstants.java @@ -87,4 +87,9 @@ public interface RuleConstants { */ String TENANT_DB_PREFIX = "sys_tenant_db_"; + /** + * base64图片前缀,用在给使用 + */ + String BASE64_IMG_PREFIX = "data:image/png;base64,"; + } diff --git a/kernel-d-auth/auth-sdk/src/main/java/cn/stylefeng/roses/kernel/auth/auth/AuthServiceImpl.java b/kernel-d-auth/auth-sdk/src/main/java/cn/stylefeng/roses/kernel/auth/auth/AuthServiceImpl.java index 77abe9fe3..8de866865 100644 --- a/kernel-d-auth/auth-sdk/src/main/java/cn/stylefeng/roses/kernel/auth/auth/AuthServiceImpl.java +++ b/kernel-d-auth/auth-sdk/src/main/java/cn/stylefeng/roses/kernel/auth/auth/AuthServiceImpl.java @@ -55,7 +55,7 @@ import cn.stylefeng.roses.kernel.jwt.api.pojo.payload.DefaultJwtPayload; import cn.stylefeng.roses.kernel.log.api.LoginLogServiceApi; import cn.stylefeng.roses.kernel.message.api.expander.WebSocketConfigExpander; import cn.stylefeng.roses.kernel.rule.util.HttpServletUtil; -import cn.stylefeng.roses.kernel.security.api.CaptchaApi; +import cn.stylefeng.roses.kernel.security.api.ImageCaptchaApi; import cn.stylefeng.roses.kernel.system.api.UserServiceApi; import cn.stylefeng.roses.kernel.system.api.enums.UserStatusEnum; import cn.stylefeng.roses.kernel.system.api.expander.SystemConfigExpander; @@ -102,7 +102,7 @@ public class AuthServiceImpl implements AuthServiceApi { private LoginLogServiceApi loginLogServiceApi; @Resource - private CaptchaApi captchaApi; + private ImageCaptchaApi captchaApi; @Resource private SsoProperties ssoProperties; diff --git a/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/DragCaptchaApi.java b/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/DragCaptchaApi.java new file mode 100644 index 000000000..2340e83ac --- /dev/null +++ b/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/DragCaptchaApi.java @@ -0,0 +1,53 @@ +/* + * Copyright [2020-2030] [https://www.stylefeng.cn] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Guns采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Guns源码头部的版权声明。 + * 3.请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://gitee.com/stylefeng/guns + * 5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/stylefeng/guns + * 6.若您的项目无法满足以上几点,可申请商业授权 + */ +package cn.stylefeng.roses.kernel.security.api; + +import cn.stylefeng.roses.kernel.security.api.pojo.DragCaptchaImageDTO; + +/** + * 拖拽验证码 + * + * @author fengshuonan + * @date 2021/7/5 12:05 + */ +public interface DragCaptchaApi { + + /** + * 生成拖拽验证码的返回值 + * + * @author fengshuonan + * @date 2021/7/5 11:55 + */ + DragCaptchaImageDTO createCaptcha(); + + /** + * 验证拖拽验证码 + * + * @author fengshuonan + * @date 2021/7/5 11:55 + */ + boolean validateCaptcha(String verKey, Integer verCode); + +} diff --git a/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/CaptchaApi.java b/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/ImageCaptchaApi.java similarity index 93% rename from kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/CaptchaApi.java rename to kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/ImageCaptchaApi.java index 035dc49c6..be4fec166 100644 --- a/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/CaptchaApi.java +++ b/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/ImageCaptchaApi.java @@ -24,7 +24,7 @@ */ package cn.stylefeng.roses.kernel.security.api; -import cn.stylefeng.roses.kernel.security.api.pojo.EasyCaptcha; +import cn.stylefeng.roses.kernel.security.api.pojo.ImageCaptcha; /** * 图形验证码Api @@ -34,7 +34,7 @@ import cn.stylefeng.roses.kernel.security.api.pojo.EasyCaptcha; * @author chenjinlong * @date 2021/1/15 13:46 */ -public interface CaptchaApi { +public interface ImageCaptchaApi { /** * 生成图形验证码 @@ -42,7 +42,7 @@ public interface CaptchaApi { * @author chenjinlong * @date 2021/1/15 12:38 */ - EasyCaptcha captcha(); + ImageCaptcha captcha(); /** * 校验图形验证码 diff --git a/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/exception/enums/SecurityExceptionEnum.java b/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/exception/enums/SecurityExceptionEnum.java index dddf354a4..4d93984dd 100644 --- a/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/exception/enums/SecurityExceptionEnum.java +++ b/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/exception/enums/SecurityExceptionEnum.java @@ -39,9 +39,9 @@ import lombok.Getter; public enum SecurityExceptionEnum implements AbstractExceptionEnum { /** - * xxx + * 生成验证码错误 */ - SECURITY_EXPIRED_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + SecurityConstants.SECURITY_EXCEPTION_STEP_CODE + "01", "安全模块异常"); + CAPTCHA_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + SecurityConstants.SECURITY_EXCEPTION_STEP_CODE + "01", "生成验证码错误"); /** * 错误编码 diff --git a/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/pojo/DragCaptchaImageDTO.java b/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/pojo/DragCaptchaImageDTO.java new file mode 100644 index 000000000..6ea86dc8f --- /dev/null +++ b/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/pojo/DragCaptchaImageDTO.java @@ -0,0 +1,48 @@ +package cn.stylefeng.roses.kernel.security.api.pojo; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * 剪裁图片dto传输实体 + * + * @author fengshuonan + * @date 2021/7/5 14:10 + */ +@Data +@AllArgsConstructor +public class DragCaptchaImageDTO { + + /** + * 本次验证码缓存的key + */ + private String key; + + /** + * 剪裁后的源图片(base64编码) + */ + private String srcImage; + + /** + * 剪裁的小拼图图片(base64编码) + */ + private String cutImage; + + /** + * x轴坐标 + */ + private Integer locationX; + + /** + * y轴坐标 + */ + private Integer locationY; + + public DragCaptchaImageDTO(String srcImage, String cutImage, int locationX, int locationY) { + this.srcImage = srcImage; + this.cutImage = cutImage; + this.locationX = locationX; + this.locationY = locationY; + } + +} \ No newline at end of file diff --git a/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/pojo/EasyCaptcha.java b/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/pojo/ImageCaptcha.java similarity index 98% rename from kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/pojo/EasyCaptcha.java rename to kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/pojo/ImageCaptcha.java index f75fd3071..ab7144883 100644 --- a/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/pojo/EasyCaptcha.java +++ b/kernel-d-security/security-api/src/main/java/cn/stylefeng/roses/kernel/security/api/pojo/ImageCaptcha.java @@ -36,7 +36,7 @@ import lombok.Data; */ @Data @Builder -public class EasyCaptcha { +public class ImageCaptcha { /** * 缓存Key diff --git a/kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/DragCaptchaService.java b/kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/DragCaptchaService.java new file mode 100644 index 000000000..f1bc4ab77 --- /dev/null +++ b/kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/DragCaptchaService.java @@ -0,0 +1,97 @@ +/* + * Copyright [2020-2030] [https://www.stylefeng.cn] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Guns采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Guns源码头部的版权声明。 + * 3.请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://gitee.com/stylefeng/guns + * 5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/stylefeng/guns + * 6.若您的项目无法满足以上几点,可申请商业授权 + */ +package cn.stylefeng.roses.kernel.security.captcha; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.stylefeng.roses.kernel.cache.api.CacheOperatorApi; +import cn.stylefeng.roses.kernel.security.api.DragCaptchaApi; +import cn.stylefeng.roses.kernel.security.api.exception.SecurityException; +import cn.stylefeng.roses.kernel.security.api.exception.enums.SecurityExceptionEnum; +import cn.stylefeng.roses.kernel.security.api.pojo.DragCaptchaImageDTO; +import cn.stylefeng.roses.kernel.security.captcha.util.DragCaptchaImageUtil; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +/** + * 拖拽验证码实现 + * + * @author fengshuonan + * @date 2021/7/5 11:34 + */ +public class DragCaptchaService implements DragCaptchaApi { + + private final CacheOperatorApi cacheOperatorApi; + + public DragCaptchaService(CacheOperatorApi cacheOperatorApi) { + this.cacheOperatorApi = cacheOperatorApi; + } + + public DragCaptchaImageDTO createCaptcha() { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.decode(DragCaptchaImageUtil.IMAGE_BASE64)); + try { + DragCaptchaImageDTO dragCaptchaImageDTO = DragCaptchaImageUtil.getVerifyImage(byteArrayInputStream); + // 缓存x轴坐标 + String verKey = IdUtil.simpleUUID(); + Integer verValue = dragCaptchaImageDTO.getLocationX(); + cacheOperatorApi.put(verKey, verValue.toString()); + + // 清空x轴坐标 + dragCaptchaImageDTO.setKey(verKey); + dragCaptchaImageDTO.setLocationX(null); + + return dragCaptchaImageDTO; + } catch (IOException e) { + throw new SecurityException(SecurityExceptionEnum.CAPTCHA_ERROR); + } finally { + IoUtil.close(byteArrayInputStream); + } + } + + public boolean validateCaptcha(String verKey, Integer verScope) { + if (StrUtil.isEmpty(verKey)) { + return false; + } + if (verScope == null) { + return false; + } + + // 获取缓存中存储的范围 + Integer locationX = Convert.toInt(cacheOperatorApi.get(verKey)); + int beginScope = locationX - 5; + int endScope = locationX + 5; + + // 每次验证不管成功和失败都剔除掉key + cacheOperatorApi.remove(verKey); + + // 验证缓存中的范围值 + return verScope >= beginScope && verScope <= endScope; + } + +} diff --git a/kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/CaptchaService.java b/kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/ImageCaptchaService.java similarity index 85% rename from kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/CaptchaService.java rename to kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/ImageCaptchaService.java index d3bd6db87..3b9e3c9a2 100644 --- a/kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/CaptchaService.java +++ b/kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/ImageCaptchaService.java @@ -27,8 +27,8 @@ package cn.stylefeng.roses.kernel.security.captcha; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.stylefeng.roses.kernel.cache.api.CacheOperatorApi; -import cn.stylefeng.roses.kernel.security.api.CaptchaApi; -import cn.stylefeng.roses.kernel.security.api.pojo.EasyCaptcha; +import cn.stylefeng.roses.kernel.security.api.ImageCaptchaApi; +import cn.stylefeng.roses.kernel.security.api.pojo.ImageCaptcha; import com.wf.captcha.SpecCaptcha; /** @@ -37,21 +37,21 @@ import com.wf.captcha.SpecCaptcha; * @author chenjinlong * @date 2021/1/15 13:44 */ -public class CaptchaService implements CaptchaApi { +public class ImageCaptchaService implements ImageCaptchaApi { private final CacheOperatorApi cacheOperatorApi; - public CaptchaService(CacheOperatorApi cacheOperatorApi) { + public ImageCaptchaService(CacheOperatorApi cacheOperatorApi) { this.cacheOperatorApi = cacheOperatorApi; } @Override - public EasyCaptcha captcha() { + public ImageCaptcha captcha() { SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5); String verCode = specCaptcha.text().toLowerCase(); String verKey = IdUtil.simpleUUID(); cacheOperatorApi.put(verKey, verCode); - return EasyCaptcha.builder().verImage(specCaptcha.toBase64()).verKey(verKey).build(); + return ImageCaptcha.builder().verImage(specCaptcha.toBase64()).verKey(verKey).build(); } @Override diff --git a/kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/util/DragCaptchaImageUtil.java b/kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/util/DragCaptchaImageUtil.java new file mode 100644 index 000000000..8294ce15e --- /dev/null +++ b/kernel-d-security/security-sdk-captcha/src/main/java/cn/stylefeng/roses/kernel/security/captcha/util/DragCaptchaImageUtil.java @@ -0,0 +1,261 @@ +package cn.stylefeng.roses.kernel.security.captcha.util; + +import cn.hutool.core.codec.Base64; +import cn.stylefeng.roses.kernel.security.api.pojo.DragCaptchaImageDTO; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * 生成拖拽验证码的工具 + * + * @author fengshuonan + * @date 2021/7/5 14:06 + */ +public class DragCaptchaImageUtil { + + /** + * 验证码图片的base64编码 + */ + public static final String IMAGE_BASE64 = "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"; + + /** + * 源文件宽度 + */ + private static final int ORI_WIDTH = 300; + + /** + * 源文件高度 + */ + private static final int ORI_HEIGHT = 150; + + /** + * 模板图宽度 + */ + private static final int CUT_WIDTH = 50; + + /** + * 模板图高度 + */ + private static final int CUT_HEIGHT = 50; + + /** + * 抠图凸起圆心 + */ + private static final int CIRCLE_R = 5; + + /** + * 抠图内部矩形填充大小 + */ + private static final int RECTANGLE_PADDING = 8; + + /** + * 抠图的边框宽度 + */ + private static final int SLIDER_IMG_OUT_PADDING = 1; + + /** + * 根据传入的路径生成指定验证码图片 + * + * @author fengshuonan + * @date 2021/7/5 14:08 + */ + public static DragCaptchaImageDTO getVerifyImage(InputStream inputStream) throws IOException { + BufferedImage srcImage = ImageIO.read(inputStream); + int locationX = CUT_WIDTH + new Random().nextInt(srcImage.getWidth() - CUT_WIDTH * 3); + int locationY = CUT_HEIGHT + new Random().nextInt(srcImage.getHeight() - CUT_HEIGHT) / 2; + BufferedImage markImage = new BufferedImage(CUT_WIDTH, CUT_HEIGHT, BufferedImage.TYPE_4BYTE_ABGR); + int[][] data = getBlockData(); + cutImgByTemplate(srcImage, markImage, data, locationX, locationY); + return new DragCaptchaImageDTO(getImageBASE64(srcImage), getImageBASE64(markImage), locationX, locationY); + } + + /** + * 生成随机滑块形状 + *

+ * 0 透明像素 + * 1 滑块像素 + * 2 阴影像素 + * + * @return int[][] + */ + private static int[][] getBlockData() { + int[][] data = new int[CUT_WIDTH][CUT_HEIGHT]; + Random random = new Random(); + //(x-a)²+(y-b)²=r² + //x中心位置左右5像素随机 + double x1 = RECTANGLE_PADDING + (CUT_WIDTH - 2 * RECTANGLE_PADDING) / 2.0 - 5 + random.nextInt(10); + //y 矩形上边界半径-1像素移动 + double y1_top = RECTANGLE_PADDING - random.nextInt(3); + double y1_bottom = CUT_HEIGHT - RECTANGLE_PADDING + random.nextInt(3); + double y1 = random.nextInt(2) == 1 ? y1_top : y1_bottom; + + + double x2_right = CUT_WIDTH - RECTANGLE_PADDING - CIRCLE_R + random.nextInt(2 * CIRCLE_R - 4); + double x2_left = RECTANGLE_PADDING + CIRCLE_R - 2 - random.nextInt(2 * CIRCLE_R - 4); + double x2 = random.nextInt(2) == 1 ? x2_right : x2_left; + double y2 = RECTANGLE_PADDING + (CUT_HEIGHT - 2 * RECTANGLE_PADDING) / 2.0 - 4 + random.nextInt(10); + + double po = Math.pow(CIRCLE_R, 2); + for (int i = 0; i < CUT_WIDTH; i++) { + for (int j = 0; j < CUT_HEIGHT; j++) { + //矩形区域 + boolean fill; + if ((i >= RECTANGLE_PADDING && i < CUT_WIDTH - RECTANGLE_PADDING) + && (j >= RECTANGLE_PADDING && j < CUT_HEIGHT - RECTANGLE_PADDING)) { + data[i][j] = 1; + fill = true; + } else { + data[i][j] = 0; + fill = false; + } + //凸出区域 + double d3 = Math.pow(i - x1, 2) + Math.pow(j - y1, 2); + if (d3 < po) { + data[i][j] = 1; + } else { + if (!fill) { + data[i][j] = 0; + } + } + //凹进区域 + double d4 = Math.pow(i - x2, 2) + Math.pow(j - y2, 2); + if (d4 < po) { + data[i][j] = 0; + } + } + } + //边界阴影 + for (int i = 0; i < CUT_WIDTH; i++) { + for (int j = 0; j < CUT_HEIGHT; j++) { + //四个正方形边角处理 + for (int k = 1; k <= SLIDER_IMG_OUT_PADDING; k++) { + //左上、右上 + if (i >= RECTANGLE_PADDING - k && i < RECTANGLE_PADDING + && ((j >= RECTANGLE_PADDING - k && j < RECTANGLE_PADDING) + || (j >= CUT_HEIGHT - RECTANGLE_PADDING - k && j < CUT_HEIGHT - RECTANGLE_PADDING + 1))) { + data[i][j] = 2; + } + + //左下、右下 + if (i >= CUT_WIDTH - RECTANGLE_PADDING + k - 1 && i < CUT_WIDTH - RECTANGLE_PADDING + 1) { + for (int n = 1; n <= SLIDER_IMG_OUT_PADDING; n++) { + if (((j >= RECTANGLE_PADDING - n && j < RECTANGLE_PADDING) + || (j >= CUT_HEIGHT - RECTANGLE_PADDING - n && j <= CUT_HEIGHT - RECTANGLE_PADDING))) { + data[i][j] = 2; + } + } + } + } + + if (data[i][j] == 1 && j - SLIDER_IMG_OUT_PADDING > 0 && data[i][j - SLIDER_IMG_OUT_PADDING] == 0) { + data[i][j - SLIDER_IMG_OUT_PADDING] = 2; + } + if (data[i][j] == 1 && j + SLIDER_IMG_OUT_PADDING > 0 && j + SLIDER_IMG_OUT_PADDING < CUT_HEIGHT && data[i][j + SLIDER_IMG_OUT_PADDING] == 0) { + data[i][j + SLIDER_IMG_OUT_PADDING] = 2; + } + if (data[i][j] == 1 && i - SLIDER_IMG_OUT_PADDING > 0 && data[i - SLIDER_IMG_OUT_PADDING][j] == 0) { + data[i - SLIDER_IMG_OUT_PADDING][j] = 2; + } + if (data[i][j] == 1 && i + SLIDER_IMG_OUT_PADDING > 0 && i + SLIDER_IMG_OUT_PADDING < CUT_WIDTH && data[i + SLIDER_IMG_OUT_PADDING][j] == 0) { + data[i + SLIDER_IMG_OUT_PADDING][j] = 2; + } + } + } + return data; + } + + /** + * 裁剪区块 + * 根据生成的滑块形状,对原图和裁剪块进行变色处理 + * + * @param oriImage 原图 + * @param targetImage 裁剪图 + * @param blockImage 滑块 + * @param x 裁剪点x + * @param y 裁剪点y + */ + private static void cutImgByTemplate(BufferedImage oriImage, BufferedImage targetImage, int[][] blockImage, int x, int y) { + for (int i = 0; i < CUT_WIDTH; i++) { + for (int j = 0; j < CUT_HEIGHT; j++) { + int _x = x + i; + int _y = y + j; + int rgbFlg = blockImage[i][j]; + int rgb_ori = oriImage.getRGB(_x, _y); + // 原图中对应位置变色处理 + if (rgbFlg == 1) { + //抠图上复制对应颜色值 + targetImage.setRGB(i, j, rgb_ori); + //原图对应位置颜色变化 + oriImage.setRGB(_x, _y, Color.LIGHT_GRAY.getRGB()); + } else if (rgbFlg == 2) { + targetImage.setRGB(i, j, Color.WHITE.getRGB()); + oriImage.setRGB(_x, _y, Color.GRAY.getRGB()); + } else if (rgbFlg == 0) { + //int alpha = 0; + targetImage.setRGB(i, j, rgb_ori & 0x00ffffff); + } + } + + } + } + + /** + * 随机获取一张图片对象 + * + * @author fengshuonan + * @date 2021/7/5 14:07 + */ + public static BufferedImage getRandomImage(String path) throws IOException { + File files = new File(path); + File[] fileList = files.listFiles(); + List fileNameList = new ArrayList<>(); + if (fileList != null && fileList.length != 0) { + for (File tempFile : fileList) { + if (tempFile.isFile() && tempFile.getName().endsWith(".jpg")) { + fileNameList.add(tempFile.getAbsolutePath().trim()); + } + } + } + Random random = new Random(); + File imageFile = new File(fileNameList.get(random.nextInt(fileNameList.size()))); + return ImageIO.read(imageFile); + } + + /** + * 将IMG输出为文件 + * + * @author fengshuonan + * @date 2021/7/5 14:07 + */ + public static void writeImg(BufferedImage image, String file) throws Exception { + byte[] imagedata = null; + ByteArrayOutputStream bao = new ByteArrayOutputStream(); + ImageIO.write(image, "png", bao); + imagedata = bao.toByteArray(); + FileOutputStream out = new FileOutputStream(new File(file)); + out.write(imagedata); + out.close(); + } + + /** + * 将图片转换为BASE64 + * + * @author fengshuonan + * @date 2021/7/5 14:07 + */ + public static String getImageBASE64(BufferedImage image) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ImageIO.write(image, "png", out); + //转成byte数组 + byte[] bytes = out.toByteArray(); + //生成BASE64编码 + return Base64.encode(bytes); + } + +} \ No newline at end of file diff --git a/kernel-d-security/security-spring-boot-starter/src/main/java/cn/stylefeng/roses/kernel/security/starter/CaptchaAutoConfiguration.java b/kernel-d-security/security-spring-boot-starter/src/main/java/cn/stylefeng/roses/kernel/security/starter/CaptchaAutoConfiguration.java index 7ecfcc856..68f939f41 100644 --- a/kernel-d-security/security-spring-boot-starter/src/main/java/cn/stylefeng/roses/kernel/security/starter/CaptchaAutoConfiguration.java +++ b/kernel-d-security/security-spring-boot-starter/src/main/java/cn/stylefeng/roses/kernel/security/starter/CaptchaAutoConfiguration.java @@ -26,8 +26,10 @@ package cn.stylefeng.roses.kernel.security.starter; import cn.hutool.cache.CacheUtil; import cn.hutool.cache.impl.TimedCache; -import cn.stylefeng.roses.kernel.security.api.CaptchaApi; -import cn.stylefeng.roses.kernel.security.captcha.CaptchaService; +import cn.stylefeng.roses.kernel.security.api.DragCaptchaApi; +import cn.stylefeng.roses.kernel.security.api.ImageCaptchaApi; +import cn.stylefeng.roses.kernel.security.captcha.DragCaptchaService; +import cn.stylefeng.roses.kernel.security.captcha.ImageCaptchaService; import cn.stylefeng.roses.kernel.security.captcha.cache.CaptchaMemoryCache; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -49,12 +51,27 @@ public class CaptchaAutoConfiguration { * @date 2021/1/15 11:25 */ @Bean - @ConditionalOnMissingBean(CaptchaApi.class) - public CaptchaApi captchaApi() { + @ConditionalOnMissingBean(ImageCaptchaApi.class) + public ImageCaptchaApi captchaApi() { // 验证码过期时间 120秒 TimedCache timedCache = CacheUtil.newTimedCache(1000 * 120); CaptchaMemoryCache captchaMemoryCache = new CaptchaMemoryCache(timedCache); - return new CaptchaService(captchaMemoryCache); + return new ImageCaptchaService(captchaMemoryCache); + } + + /** + * 拖拽验证码工具 + * + * @author fengshuonan + * @date 2021/7/5 11:57 + */ + @Bean + @ConditionalOnMissingBean(DragCaptchaApi.class) + public DragCaptchaApi dragCaptchaService() { + // 验证码过期时间 120秒 + TimedCache timedCache = CacheUtil.newTimedCache(1000 * 120); + CaptchaMemoryCache captchaMemoryCache = new CaptchaMemoryCache(timedCache); + return new DragCaptchaService(captchaMemoryCache); } } diff --git a/kernel-d-sms/sms-business-validation/src/main/java/cn/stylefeng/roses/kernel/sms/modular/service/impl/SysSmsInfoServiceImpl.java b/kernel-d-sms/sms-business-validation/src/main/java/cn/stylefeng/roses/kernel/sms/modular/service/impl/SysSmsInfoServiceImpl.java index 871366ab0..58dc4afd9 100644 --- a/kernel-d-sms/sms-business-validation/src/main/java/cn/stylefeng/roses/kernel/sms/modular/service/impl/SysSmsInfoServiceImpl.java +++ b/kernel-d-sms/sms-business-validation/src/main/java/cn/stylefeng/roses/kernel/sms/modular/service/impl/SysSmsInfoServiceImpl.java @@ -31,7 +31,7 @@ import cn.hutool.core.util.StrUtil; import cn.stylefeng.roses.kernel.db.api.factory.PageFactory; import cn.stylefeng.roses.kernel.db.api.factory.PageResultFactory; import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult; -import cn.stylefeng.roses.kernel.security.api.CaptchaApi; +import cn.stylefeng.roses.kernel.security.api.ImageCaptchaApi; import cn.stylefeng.roses.kernel.sms.api.SmsSenderApi; import cn.stylefeng.roses.kernel.sms.api.exception.SmsException; import cn.stylefeng.roses.kernel.sms.api.expander.SmsConfigExpander; @@ -75,7 +75,7 @@ public class SysSmsInfoServiceImpl extends ServiceImpl imp private SmsSenderApi smsSenderApi; @Resource - private CaptchaApi captchaApi; + private ImageCaptchaApi captchaApi; @Transactional(rollbackFor = Exception.class) @Override diff --git a/kernel-s-system/system-business-user/src/main/java/cn/stylefeng/roses/kernel/system/modular/user/controller/KaptchaController.java b/kernel-s-system/system-business-user/src/main/java/cn/stylefeng/roses/kernel/system/modular/user/controller/KaptchaController.java index 7cf3e2c79..1adf03f3f 100644 --- a/kernel-s-system/system-business-user/src/main/java/cn/stylefeng/roses/kernel/system/modular/user/controller/KaptchaController.java +++ b/kernel-s-system/system-business-user/src/main/java/cn/stylefeng/roses/kernel/system/modular/user/controller/KaptchaController.java @@ -28,12 +28,16 @@ import cn.stylefeng.roses.kernel.rule.pojo.response.ResponseData; import cn.stylefeng.roses.kernel.rule.pojo.response.SuccessResponseData; import cn.stylefeng.roses.kernel.scanner.api.annotation.ApiResource; import cn.stylefeng.roses.kernel.scanner.api.annotation.GetResource; -import cn.stylefeng.roses.kernel.security.api.CaptchaApi; -import cn.stylefeng.roses.kernel.security.api.pojo.EasyCaptcha; +import cn.stylefeng.roses.kernel.security.api.DragCaptchaApi; +import cn.stylefeng.roses.kernel.security.api.ImageCaptchaApi; +import cn.stylefeng.roses.kernel.security.api.pojo.DragCaptchaImageDTO; +import cn.stylefeng.roses.kernel.security.api.pojo.ImageCaptcha; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; +import static cn.stylefeng.roses.kernel.rule.constants.RuleConstants.BASE64_IMG_PREFIX; + /** * 图形验证码 * @@ -45,11 +49,34 @@ import javax.annotation.Resource; public class KaptchaController { @Resource - private CaptchaApi captchaApi; + private ImageCaptchaApi captchaApi; - @GetResource(name = "获取图形验证码", path = "/captcha", requiredPermission = false, requiredLogin = false, responseClass = EasyCaptcha.class) + @Resource + private DragCaptchaApi dragCaptchaApi; + + /** + * 获取图形验证码 + * + * @author fengshuonan + * @date 2021/7/5 12:00 + */ + @GetResource(name = "获取图形验证码", path = "/captcha", requiredPermission = false, requiredLogin = false, responseClass = ImageCaptcha.class) public ResponseData captcha() { return new SuccessResponseData(captchaApi.captcha()); } + /** + * 获取拖拽验证码 + * + * @author fengshuonan + * @date 2021/7/5 12:00 + */ + @GetResource(name = "获取图形验证码", path = "/dragCaptcha", requiredPermission = false, requiredLogin = false, responseClass = DragCaptchaImageDTO.class) + public ResponseData dragCaptcha() { + DragCaptchaImageDTO captcha = dragCaptchaApi.createCaptcha(); + captcha.setSrcImage(BASE64_IMG_PREFIX + captcha.getSrcImage()); + captcha.setCutImage(BASE64_IMG_PREFIX + captcha.getCutImage()); + return new SuccessResponseData(captcha); + } + }