Selaa lähdekoodia

短信验证码

tudc 3 vuotta sitten
vanhempi
commit
14e8e77e56

+ 6 - 0
sc-service/pom.xml

@@ -151,6 +151,12 @@
             <artifactId>gson</artifactId>
             <version>2.8.5</version>
         </dependency>
+
+        <!--验证码 -->
+        <dependency>
+            <groupId>com.github.penggle</groupId>
+            <artifactId>kaptcha</artifactId>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

+ 8 - 2
sc-service/src/main/java/com/huyi/service/base/entity/PubVerifyCode.java

@@ -14,8 +14,8 @@ import lombok.experimental.Accessors;
  * 短信验证码信息表
  * </p>
  *
- * @author xuefy
- * @since 2021-08-16
+ * @author cuixq
+ * @since 2021-08-19
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -38,6 +38,12 @@ public class PubVerifyCode implements Serializable {
     private String pvcCode;
 
     /**
+     * 短信内容
+     */
+    @TableField("pvc_content")
+    private String pvcContent;
+
+    /**
      * 手机号
      */
     @TableField("pvc_phone")

+ 3 - 3
sc-service/src/main/java/com/huyi/service/base/mapper/PubVerifyCodeMapper.java

@@ -1,15 +1,15 @@
 package com.huyi.service.base.mapper;
 
-import com.huyi.service.base.entity.PubVerifyCode;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.huyi.service.base.entity.PubVerifyCode;
 
 /**
  * <p>
  * 短信验证码信息表 Mapper 接口
  * </p>
  *
- * @author xuefy
- * @since 2021-08-16
+ * @author cuixq
+ * @since 2021-08-19
  */
 public interface PubVerifyCodeMapper extends BaseMapper<PubVerifyCode> {
 

+ 3 - 3
sc-service/src/main/java/com/huyi/service/base/service/IPubVerifyCodeService.java

@@ -1,7 +1,7 @@
 package com.huyi.service.base.service;
 
-import com.huyi.service.base.entity.PubVerifyCode;
 
+import com.huyi.service.base.entity.PubVerifyCode;
 import com.keao.tianhu.starter.mybatis.plus.entity.QueryRequest;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -16,8 +16,8 @@ import java.util.List;
  * Description: PubVerifyCodeService接口 <br>
  * Company: keao
  *
- * @author xuefy
- * @version v1.0.0    2021-08-16  xuefy    由Generator自动创建
+ * @author cuixq
+ * @version v1.0.0    2021-08-19  cuixq    由Generator自动创建
  */
 public interface IPubVerifyCodeService extends IService<PubVerifyCode> {
     /**

+ 2 - 2
sc-service/src/main/java/com/huyi/service/base/service/impl/PubVerifyCodeServiceImpl.java

@@ -23,8 +23,8 @@ import java.util.List;
  * Description: IPubVerifyCodeService实现 <br>
  * Company: keao
  *
- * @author xuefy
- * @version v1.0.0    2021-08-16  xuefy    由Generator自动创建
+ * @author cuixq
+ * @version v1.0.0    2021-08-19  cuixq    由Generator自动创建
  */
 @Service
 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)

+ 33 - 0
sc-service/src/main/java/com/huyi/service/common/ShortMessageController.java

@@ -0,0 +1,33 @@
+package com.huyi.service.common;
+
+import com.huyi.service.util.ValidateCodeService;
+import com.huyi.service.util.impl.MessageUtilsServiceImpl;
+import com.tianhu.common.core.web.domain.AjaxResult;
+import com.tianhu.common.log.annotation.Log;
+import com.tianhu.common.log.enums.BusinessType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/shortMessage")
+public class ShortMessageController {
+    @Autowired
+    MessageUtilsServiceImpl messageUtilsService;
+    @Autowired
+    private ValidateCodeService validateCodeService;
+
+
+    @Log(title = "发送短信", businessType = BusinessType.INSERT)
+    @Transactional(rollbackFor=Exception.class)
+    @PostMapping("/sendShortMessage")
+    public AjaxResult sendShortMessage(@RequestBody Map<String, String> requestBody,
+                                       HttpServletRequest request) {
+        validateCodeService.checkCapcha(requestBody.get("imgCode"), requestBody.get("uuid"));
+        messageUtilsService.sendMessageCode(requestBody.get("phone"), requestBody.get("verificationCode"), request);
+        return AjaxResult.success();
+    }
+}

+ 85 - 0
sc-service/src/main/java/com/huyi/service/config/properties/CaptchaConfig.java

@@ -0,0 +1,85 @@
+package com.huyi.service.config.properties;
+
+import com.google.code.kaptcha.impl.DefaultKaptcha;
+import com.google.code.kaptcha.util.Config;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Properties;
+
+import static com.google.code.kaptcha.Constants.*;
+
+/**
+ * 验证码配置
+ * 
+ * @author tianhu
+ */
+@Configuration
+public class CaptchaConfig
+{
+    @Bean(name = "captchaProducer")
+    public DefaultKaptcha getKaptchaBean()
+    {
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        Properties properties = new Properties();
+        // 是否有边框 默认为true 我们可以自己设置yes,no
+        properties.setProperty(KAPTCHA_BORDER, "yes");
+        // 验证码文本字符颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
+        // 验证码图片宽度 默认为200
+        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
+        // 验证码图片高度 默认为50
+        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
+        // 验证码文本字符大小 默认为40
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
+        // KAPTCHA_SESSION_KEY
+        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
+        // 验证码文本字符长度 默认为5
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
+        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
+        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
+        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
+        Config config = new Config(properties);
+        defaultKaptcha.setConfig(config);
+        return defaultKaptcha;
+    }
+    
+    @Bean(name = "captchaProducerMath")
+    public DefaultKaptcha getKaptchaBeanMath()
+    {
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        Properties properties = new Properties();
+        // 是否有边框 默认为true 我们可以自己设置yes,no
+        properties.setProperty(KAPTCHA_BORDER, "yes");
+        // 边框颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
+        // 验证码文本字符颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
+        // 验证码图片宽度 默认为200
+        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
+        // 验证码图片高度 默认为50
+        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
+        // 验证码文本字符大小 默认为40
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
+        // KAPTCHA_SESSION_KEY
+        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
+        // 验证码文本生成器
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.tianhu.gateway.config.KaptchaTextCreator");
+        // 验证码文本字符间距 默认为2
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
+        // 验证码文本字符长度 默认为5
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
+        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
+        // 验证码噪点颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
+        // 干扰实现类
+        properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
+        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
+        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
+        Config config = new Config(properties);
+        defaultKaptcha.setConfig(config);
+        return defaultKaptcha;
+    }
+}

+ 32 - 0
sc-service/src/main/java/com/huyi/service/constant/MessageConstants.java

@@ -0,0 +1,32 @@
+package com.huyi.service.constant;
+
+
+
+/**
+ * 消息发送常量类
+ *
+ */
+public class MessageConstants {
+	
+	/**
+	 * 最后一次发送短信时间KEY
+	 */
+	static final public String LAST_MOBILE_TIME = "LAST_MOBILE_TIME";
+	/**
+	 * 最后一次发送短信时间KEY
+	 */
+	static final public String MOBILE_RANDOM = "MOBILE_RANDOM";
+	/**
+	 * 最后一次发送短信的手机号KEY
+	 */
+	static final public String MOBILE_NO = "MOBILE_NO";
+	/**
+	 * 常规短信验证码格式
+	 */
+	static final public String REG_MOBILE_MODE = "验证码:@,如非本人操作,请忽略本短信";
+	
+}
+
+
+
+

+ 7 - 0
sc-service/src/main/java/com/huyi/service/util/IMessageUtilsService.java

@@ -0,0 +1,7 @@
+package com.huyi.service.util;
+
+import javax.servlet.http.HttpServletRequest;
+
+public interface IMessageUtilsService {
+    void sendMessageCode(String mobileNo, String verification_code, HttpServletRequest request) throws Exception;
+}

+ 24 - 0
sc-service/src/main/java/com/huyi/service/util/ValidateCodeService.java

@@ -0,0 +1,24 @@
+package com.huyi.service.util;
+
+import com.tianhu.common.core.exception.CaptchaException;
+import com.tianhu.common.core.web.domain.AjaxResult;
+
+import java.io.IOException;
+
+/**
+ * 验证码处理
+ * 
+ * @author tianhu
+ */
+public interface ValidateCodeService
+{
+    /**
+     * 生成验证码
+     */
+    public AjaxResult createCapcha() throws IOException, CaptchaException;
+
+    /**
+     * 校验验证码
+     */
+    public void checkCapcha(String key, String value) throws CaptchaException;
+}

+ 148 - 0
sc-service/src/main/java/com/huyi/service/util/impl/MessageUtilsServiceImpl.java

@@ -0,0 +1,148 @@
+package com.huyi.service.util.impl;
+
+import cn.hutool.http.HttpUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.huyi.service.base.entity.PubVerifyCode;
+import com.huyi.service.base.service.IPubVerifyCodeService;
+import com.huyi.service.constant.MessageConstants;
+import com.huyi.service.util.IMessageUtilsService;
+import com.tianhu.common.core.exception.BaseException;
+import com.tianhu.common.core.utils.CommonUtil;
+import com.tianhu.common.core.utils.DateUtils;
+import com.tianhu.common.core.utils.IdUtils;
+import com.tianhu.common.redis.common.RedisUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Random;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 消息推送工具类
+ *
+ * @author tianhu
+ */
+@Service
+public class MessageUtilsServiceImpl implements IMessageUtilsService
+{
+    @Autowired
+    private IPubVerifyCodeService iPubVerifyCodeService;
+    //手机号格式校验
+    public static String MOBILE = "^1\\d{10}$";
+    //验证码状态(未校验)
+    static final String  NO_CHECK = "00";
+
+    /**
+     * 发送短信验证码
+     * @param mobileNo 手机号码
+     * @param verificationCode 模板id
+     * @param request
+     * @return
+     * @throws Exception
+     */
+    @Override
+    public void sendMessageCode(String mobileNo, String verificationCode, HttpServletRequest request)
+    {
+        if (CommonUtil.isEmpty(mobileNo)) {
+            throw new BaseException("手机号不能为空!");
+        } else {
+            // 校验手机号
+            Pattern pattern = Pattern.compile(MOBILE);
+            Matcher matcher = pattern.matcher(mobileNo);
+            boolean rs = matcher.matches();
+            if (!rs) {
+                throw new BaseException("手机号格式错误");
+            }
+        }
+
+        // 验证是否已经发送验证码
+        HttpSession httpSession = request.getSession();
+        //查询验证码
+        LambdaQueryWrapper<PubVerifyCode> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(PubVerifyCode::getPvcPhone,mobileNo);
+        queryWrapper.eq(PubVerifyCode::getPvcState,NO_CHECK);
+        queryWrapper.orderByDesc(PubVerifyCode::getPvcLastTime);
+        List<PubVerifyCode> staffInfList = iPubVerifyCodeService.findPubVerifyCodes(queryWrapper);
+        // 如果已经发送过短`信验证码,并且在60S以内,稍后再发送
+        if(staffInfList.size()>0){
+            //取得指定时间间隔后的系统时间
+            GregorianCalendar calendar = (GregorianCalendar) Calendar.getInstance();
+            calendar.add( Calendar.MINUTE, -1);
+            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
+            System.out.println("比较时间"+formatter.format(calendar.getTime()));
+            System.out.println("最后时间"+formatter.format(staffInfList.get(0).getPvcLastTime()));
+            if(formatter.format(calendar.getTime()).compareTo(formatter.format(staffInfList.get(0).getPvcLastTime()))<0){
+                System.out.println("请勿频繁发送验证码");
+                throw new BaseException("请勿频繁发送验证码!");
+            }
+        }
+
+        // 获取随机数
+        String random = genRandomNum(6);
+        //调试模式
+        boolean isTest = true;
+        // 获取验证码时间
+        httpSession.setAttribute(MessageConstants.LAST_MOBILE_TIME, DateUtils.getNowDate());
+        // 验证码
+        httpSession.setAttribute(MessageConstants.MOBILE_RANDOM, random);
+        // 手机号
+        httpSession.setAttribute(MessageConstants.MOBILE_NO, mobileNo);
+        String regVal = RedisUtils.getDictValue("pub_verification_code", verificationCode);
+        // 发送短信
+        String msg = regVal.replaceAll("@", random);
+
+        PubVerifyCode pubVerifyCode = new PubVerifyCode();
+        pubVerifyCode.setPvcId(IdUtils.fastSimpleUUID());
+        pubVerifyCode.setPvcCode(random);
+        pubVerifyCode.setPvcPhone(mobileNo);
+        pubVerifyCode.setPvcLastTime(DateUtils.getNowDate());
+        pubVerifyCode.setPvcState("00");
+        iPubVerifyCodeService.createPubVerifyCode(pubVerifyCode);
+		if(!isTest) {
+			System.out.println("mobileNo:"+mobileNo+"====ccc======msg:"+msg);
+			StringBuilder sb = new StringBuilder();
+			sb.append("mobile=");
+			sb.append(mobileNo);
+			sb.append("&content=");
+			sb.append(msg);
+			sb.append("&appkey=");
+            String appKey = RedisUtils.getDictValue("pub_jisu_app_key", "000000_app_key");
+			sb.append(appKey);
+			try {
+				System.out.println("======sb:"+ sb.toString());
+				HttpUtil.get("http://api.jisuapi.com/sms/send?" + sb.toString());
+			} catch (Exception ex) {
+                System.out.println("获取短信验证码失败");
+				throw new BaseException("获取短信验证码失败");
+			}
+		}
+    }
+
+    public static String genRandomNum(int card_len){
+        //35是因为数组是从0开始的,26个字母+10个数字
+        final int maxNum = 36;
+        //生成的随机数
+        int i;
+        //生成的密码的长度
+        int count = 0;
+        char[] str = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
+        StringBuffer pwd = new StringBuffer("");
+        Random r = new Random();
+        while(count < card_len){
+            //生成随机数,取绝对值,防止生成负数 生成的数最大为36-1
+            i = Math.abs(r.nextInt(maxNum));
+            if (i >= 0 && i < str.length) {
+                pwd.append(str[i]);
+                count ++;
+            }
+        }
+        return pwd.toString();
+    }
+}

+ 110 - 0
sc-service/src/main/java/com/huyi/service/util/impl/ValidateCodeServiceImpl.java

@@ -0,0 +1,110 @@
+package com.huyi.service.util.impl;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Resource;
+import javax.imageio.ImageIO;
+
+import com.huyi.service.util.ValidateCodeService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.FastByteArrayOutputStream;
+import com.google.code.kaptcha.Producer;
+import com.tianhu.common.core.constant.Constants;
+import com.tianhu.common.core.exception.CaptchaException;
+import com.tianhu.common.core.utils.IdUtils;
+import com.tianhu.common.core.utils.StringUtils;
+import com.tianhu.common.core.utils.sign.Base64;
+import com.tianhu.common.core.web.domain.AjaxResult;
+import com.tianhu.common.redis.service.RedisService;
+
+/**
+ * 验证码实现处理
+ *
+ * @author tianhu
+ */
+@Service
+public class ValidateCodeServiceImpl implements ValidateCodeService
+{
+    @Resource(name = "captchaProducer")
+    private Producer captchaProducer;
+
+    @Resource(name = "captchaProducerMath")
+    private Producer captchaProducerMath;
+
+    @Autowired
+    private RedisService redisService;
+
+    // 验证码类型
+    private String captchaType = "math";
+
+    /**
+     * 生成验证码
+     */
+    @Override
+    public AjaxResult createCapcha() throws IOException, CaptchaException
+    {
+        // 保存验证码信息
+        String uuid = IdUtils.simpleUUID();
+        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
+
+        String capStr = null, code = null;
+        BufferedImage image = null;
+
+        // 生成验证码
+        if ("math".equals(captchaType))
+        {
+            String capText = captchaProducerMath.createText();
+            capStr = capText.substring(0, capText.lastIndexOf("@"));
+            code = capText.substring(capText.lastIndexOf("@") + 1);
+            image = captchaProducerMath.createImage(capStr);
+        }
+        else if ("char".equals(captchaType))
+        {
+            capStr = code = captchaProducer.createText();
+            image = captchaProducer.createImage(capStr);
+        }
+
+        redisService.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
+        // 转换流信息写出
+        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
+        try
+        {
+            ImageIO.write(image, "jpg", os);
+        }
+        catch (IOException e)
+        {
+            return AjaxResult.error(e.getMessage());
+        }
+
+        AjaxResult ajax = AjaxResult.success();
+        ajax.put("uuid", uuid);
+        ajax.put("img", Base64.encode(os.toByteArray()));
+        return ajax;
+    }
+
+    /**
+     * 校验验证码
+     */
+    @Override
+    public void checkCapcha(String code, String uuid) throws CaptchaException
+    {
+        if (StringUtils.isEmpty(code))
+        {
+            throw new CaptchaException("验证码不能为空");
+        }
+        if (StringUtils.isEmpty(uuid))
+        {
+            throw new CaptchaException("验证码已失效");
+        }
+        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
+        String captcha = redisService.getCacheObject(verifyKey);
+        redisService.deleteObject(verifyKey);
+
+        if (!code.equalsIgnoreCase(captcha))
+        {
+            throw new CaptchaException("验证码错误");
+        }
+    }
+}

+ 1 - 0
sc-service/src/main/resources/mapper/base/PubVerifyCodeMapper.xml

@@ -6,6 +6,7 @@
     <resultMap id="BaseResultMap" type="com.huyi.service.base.entity.PubVerifyCode">
         <id column="pvc_id" property="pvcId" />
         <result column="pvc_code" property="pvcCode" />
+        <result column="pvc_content" property="pvcContent" />
         <result column="pvc_phone" property="pvcPhone" />
         <result column="pvc_last_time" property="pvcLastTime" />
         <result column="pvc_state" property="pvcState" />