Przeglądaj źródła

fix 获取验证码校验恶意手机号

zoujiajian 3 lat temu
rodzic
commit
5f06e83cc9

+ 18 - 5
netflix-common/src/main/java/com/cyksj/common/util/SmsUtil.java

@@ -11,6 +11,7 @@ import com.tencentcloudapi.common.profile.HttpProfile;
 import com.tencentcloudapi.sms.v20210111.SmsClient;
 import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
 import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
+import com.tencentcloudapi.sms.v20210111.models.SendStatus;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -60,14 +61,20 @@ public class SmsUtil {
         System.out.println(execute.body());
     }
 
-    public static void sendLuoKey2Msg(String mobile, String msg) {
+    public static Boolean sendLuoKey2Msg(String mobile, String msg) throws Exception {
         HttpRequest request = new HttpRequest(URL);
         request.setMethod(Method.POST);
         request.basicAuth("api", LVSINCO_PASSWORD);
         request.form("mobile", mobile);
         request.form("message", msg);
         HttpResponse execute = request.execute();
-        System.out.println(execute.body());
+        JSONObject re = Jsons.parseObject(execute.body(), JSONObject.class);
+        if (re.getInt("error") == 0) {
+            log.info(execute.body());
+            return true;
+        }
+        log.error(execute.body());
+        return false;
     }
 
     public static Boolean batchSendLuoKey2Msg(List<String> mobile, String msg) throws Exception {
@@ -86,7 +93,7 @@ public class SmsUtil {
         return false;
     }
 
-    public static void sendTencent(Integer cid, String mobile, String msg) {
+    public static Boolean sendTencent(Integer cid, String mobile, String msg) {
         try {
             /* 必要步骤:
              * 实例化一个认证对象,入参需要传入腾讯云账户密钥对secretId,secretKey。
@@ -150,9 +157,14 @@ public class SmsUtil {
             /* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的
              * 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应 */
             SendSmsResponse res = client.SendSms(req);
-
             // 输出json格式的字符串回包
-            System.out.println(SendSmsResponse.toJsonString(res));
+            log.info(SendSmsResponse.toJsonString(res));
+            SendStatus[] sendStatusSet = res.getSendStatusSet();
+            SendStatus sendStatus = sendStatusSet[0];
+            if ("Ok".equalsIgnoreCase(sendStatus.getCode())) {
+                return true;
+            }
+            return false;
 
             // 也可以取出单个值,你可以通过官网接口文档或跳转到response对象的定义处查看返回字段的定义
             // System.out.println(res.getRequestId());
@@ -168,5 +180,6 @@ public class SmsUtil {
         } catch (TencentCloudSDKException e) {
             log.error("发送腾讯云短信错误,e:{}", e);
         }
+        return false;
     }
 }

+ 3 - 0
netflix-service/src/main/java/com/cyksj/redis/RedisService.java

@@ -595,6 +595,9 @@ public class RedisService {
         WX_LOGIN_AUTH_URI("wx_login_auth_uri:%s", "微信网页授权登录uri", 60 * 60 * 24L),
         RECOMMEND_COUPON_DAY("recommend_coupon_day:%s", "推荐优惠券弹窗", 60 * 60 * 24L),
         EMAIL_CODE_KEY("email_code_key:%s", "邮箱验证码", 60 * 3),
+        PHONE_CODE_KEY("phone_code_key:", "短信验证码", 60 * 3),
+        PHONE_CODE_USER_NUM_KEY_DAY("phone_code_user_num_key:", "近一天获取验证码次数", 60 * 60 * 24l),
+        PHONE_CODE_BALCK_KEY("phone_code_balck_key:", "手机黑名单", -1l),
         LOTTERY_KEY("lottery_key:%s", "抽奖key", 60 * 60 * 24l),
         TASK_TIME_LIMIT_KEY("task_time_limit:%s", "一次性任务key", 30),
         WX_GZH_QRCODE_LOGIN("wx_gzh_qrcode_login:%s", "微信公众号扫码登录key", 60),

+ 1 - 1
netflix-service/src/main/java/com/cyksj/service/mange/CmsGroupService.java

@@ -14,7 +14,7 @@ public interface CmsGroupService extends IService<GroupsTrips> {
 
     void close(Long groupId);
 
-    void setAccount(GroupsTrips groupsTrips);
+    void setAccount(GroupsTrips groupsTrips) throws Exception;
 
     void replaceTripsAccount(ReplaceTripsAccountReq req);
 }

+ 5 - 2
netflix-service/src/main/java/com/cyksj/service/mange/group/CmsGroupServiceImpl.java

@@ -152,7 +152,7 @@ public class CmsGroupServiceImpl extends ServiceImpl<GroupsMapper,GroupsTrips> i
 
     @Override
     @Transactional(rollbackFor = Throwable.class)
-    public void setAccount(GroupsTrips groupsTrips) {
+    public void setAccount(GroupsTrips groupsTrips) throws Exception {
         int count = count(Wrappers.lambdaQuery(GroupsTrips.class).eq(GroupsTrips::getAccountId, groupsTrips.getAccountId()));
         if (count > 0) {
             throw new BusinessRuntimeException("该账号已发车.请选择未发车账户");
@@ -329,7 +329,10 @@ public class CmsGroupServiceImpl extends ServiceImpl<GroupsMapper,GroupsTrips> i
             }
             if (relation.getUserId() != 0 && isPrd) {
                 String msg = String.format(Constant.CHANGE_GROUPS_TRIPS_ACCOUNT, title);
-                smsService.sendSmsToRelationUser(relation.getUserId(), msg);
+                try {
+                    smsService.sendSmsToRelationUser(relation.getUserId(), msg);
+                } catch (Exception e) {
+                }
             }
         });
     }

+ 1 - 1
netflix-service/src/main/java/com/cyksj/service/sms/SmsService.java

@@ -11,5 +11,5 @@ public interface SmsService {
 	/**
 	 * 发送车队短信通知 车位用户
 	 */
-	void sendSmsToRelationUser(Long userId, String msg);
+	void sendSmsToRelationUser(Long userId, String msg) throws Exception;
 }

+ 1 - 1
netflix-service/src/main/java/com/cyksj/service/sms/impl/SmsServiceImpl.java

@@ -21,7 +21,7 @@ public class SmsServiceImpl implements SmsService {
 	private final GroupsRelationMapper groupsRelationMapper;
 
 	@Override
-	public void sendSmsToRelationUser(Long userId, String msg) {
+	public void sendSmsToRelationUser(Long userId, String msg) throws Exception {
 		String phone = groupsRelationMapper.getBindPhoneByUserId(userId);
 		if (StrUtil.isNotBlank(phone)) {
 			SmsUtil.sendLuoKey2Msg(phone, msg);

+ 5 - 0
netflix-service/src/main/java/com/cyksj/service/user/BadIntentionOpService.java

@@ -0,0 +1,5 @@
+package com.cyksj.service.user;
+
+public interface BadIntentionOpService {
+    void checkHandleBadUserOp(String phone);
+}

+ 40 - 0
netflix-service/src/main/java/com/cyksj/service/user/impl/BadIntentionOpServiceImpl.java

@@ -0,0 +1,40 @@
+package com.cyksj.service.user.impl;
+
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.redis.RedisService;
+import com.cyksj.service.user.BadIntentionOpService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class BadIntentionOpServiceImpl implements BadIntentionOpService {
+    private final RedisService redisService;
+
+    private static final int PHONE_CODE_GET_NUM_DAY_LIMIT = 30;
+    private static final int PHONE_CODE_GET_NUM_DAY_MAX_BLACK = 50;
+
+    @Override
+    public void checkHandleBadUserOp(String phone) {
+        if ("13662979985".equals(phone)) {
+            throw BusinessRuntimeException.getInstance("异常手机号");
+        }
+        String dayKey = RedisService.key.PHONE_CODE_USER_NUM_KEY_DAY.getName() + phone;
+        if (redisService.hasKey(dayKey)) {
+            Long incr = redisService.incr(dayKey, 1l);
+            if (incr.intValue() >= PHONE_CODE_GET_NUM_DAY_MAX_BLACK) {
+                log.error("近一天内手机号:{}获取验证码超过50次,移入黑名单", phone);
+                redisService.set(RedisService.key.PHONE_CODE_BALCK_KEY.getName() + phone, phone);
+            }
+
+            if (incr.intValue() > PHONE_CODE_GET_NUM_DAY_LIMIT) {
+                log.error("手机号:{}频繁获取短信验证码", phone);
+                throw BusinessRuntimeException.getInstance("请勿频繁获取短信验证码");
+            }
+            return;
+        }
+        redisService.set(dayKey, 1, RedisService.key.PHONE_CODE_USER_NUM_KEY_DAY.getTimeout());
+    }
+}

+ 2 - 2
netflix-web/src/main/java/com/cyksj/web/controller/manage/group/CmsGroupController.java

@@ -129,7 +129,7 @@ public class CmsGroupController {
      */
     @PutMapping("/update/set/account")
     @Log(module = "发布车票/配置账号", businessType = BusinessType.PUT, isSaveRequestData = true)
-    public Result<String> setAccount(@RequestBody GroupsTrips groupsTrips){
+    public Result<String> setAccount(@RequestBody GroupsTrips groupsTrips) throws Exception {
         Account account = accountService.getById(groupsTrips.getAccountId());
         if(account == null){
             throw new BusinessRuntimeException("id : " + groupsTrips.getAccount() + " 该账号不存在!");
@@ -143,7 +143,7 @@ public class CmsGroupController {
      * 配置新账号并绑定车队
      */
     @PostMapping("/post/account/bind")
-    public Result<String> createNewAccountBind(@RequestBody @Validated AccountGroupsReq req) {
+    public Result<String> createNewAccountBind(@RequestBody @Validated AccountGroupsReq req) throws Exception {
         Account account = new Account();
         BeanUtil.copyProperties(req, account);
         GroupsTrips groupsTrips = groupService.getById(req.getGroupsId());

+ 35 - 19
netflix-web/src/main/java/com/cyksj/web/controller/user/AuthorizationController.java

@@ -27,12 +27,14 @@ import com.cyksj.service.authorization.AuthorizationService;
 import com.cyksj.service.distribute.DistributeService;
 import com.cyksj.service.mail.MailService;
 import com.cyksj.service.template.TemplateCommonService;
+import com.cyksj.service.user.BadIntentionOpService;
 import com.cyksj.service.user.UserService;
 import com.cyksj.service.wechat.WeChatService;
 import com.cyksj.web.util.StpUserUtil;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.dao.DuplicateKeyException;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
 
@@ -67,6 +69,8 @@ public class AuthorizationController {
 
     private final RedisService redisService;
 
+    private final BadIntentionOpService badIntentionOpService;
+
     private final WxOpenPlatYHLXLoginConfig wxOpenPlatYHLXLoginConfig;
 
     private final EnvCommonService envCommonService;
@@ -77,10 +81,6 @@ public class AuthorizationController {
 
     private final TemplateCommonService templateCommonService;
 
-    private final DistributeService distributeService;
-
-    private final UserDistributePopularizeMapper distributePopularizeMapper;
-
     @GetMapping(value = "/weChat")
     public void base(String url, String dsCode, Long sharedId, Integer dsType, Long popularizeId, String authType, String callbackType, String callback, HttpServletResponse response) throws Exception {
 
@@ -309,34 +309,47 @@ public class AuthorizationController {
      */
     @GetMapping("/get/phone/code")
     @NoSubmit
-    public Result<String> getPhoneCode(String phone, Integer digit, @RequestParam(defaultValue = "0") Boolean isFree, Integer cid) {
+    public Result<String> getPhoneCode(String phone, Integer digit, @RequestParam(defaultValue = "0") Boolean isFree, Integer cid) throws Exception {
         if (cid == null) cid = 86;
         if (StrUtil.isBlank(phone) || (86 == cid && !Validator.isMobile(phone))) {
             throw BusinessRuntimeException.getInstance("请输入正确的手机号");
         }
-        if ("13662979985".equals(phone)) {
-            throw BusinessRuntimeException.getInstance("异常手机号");
-        }
         //是否是虚拟手机号开头
         if (BlackOpsUtil.phoneIsStart(phone)) {
             throw BusinessRuntimeException.getInstance("请输入正确的手机号");
         }
-        PhoneCode phoneCode = phoneCodeMapper.selectOne(Wrappers.lambdaQuery(PhoneCode.class).eq(PhoneCode::getPhone, phone).last("limit 1"));
-        if (phoneCode == null) {
-            phoneCode = new PhoneCode();
-            phoneCode.setPhone(phone);
+        Object exists = redisService.get(RedisService.key.PHONE_CODE_KEY.getName() + phone);
+        if (exists != null) {
+            throw BusinessRuntimeException.getInstance("短信验证码未失效");
         }
+        //是否是恶意用户
+        badIntentionOpService.checkHandleBadUserOp(phone);
         String code = StringUtil.getRandomCodeStr(digit);
-        phoneCode.setCode(code);
-        if (phoneCode.getId() == null) {
-            phoneCode.setCid(cid);
-            phoneCodeMapper.insert(phoneCode);
-        } else phoneCodeMapper.updateById(phoneCode);
+        Boolean isFlag;
         if (!isFree) {
-            SmsUtil.sendTencent(cid, phone, code);
+            isFlag = SmsUtil.sendTencent(cid, phone, code);
         } else {
             // 发送短信
-            SmsUtil.sendLuoKey2Msg(phone, String.format("尊敬的用户,您的验证码为%s,此验证码仅用于银河录像。【银河录像局】", code));
+            isFlag = SmsUtil.sendLuoKey2Msg(phone, String.format("尊敬的用户,您的验证码为%s,此验证码仅用于银河录像。【银河录像局】", code));
+        }
+        if (!isFlag) {
+            return GatewayResponse.SUCCESS.newBuilder().toResult("发送短信验证码失败");
+        }
+        redisService.setNx(RedisService.key.PHONE_CODE_KEY.getName() + phone, phone, RedisService.key.PHONE_CODE_KEY.getTimeout());
+        try {
+            PhoneCode phoneCode = phoneCodeMapper.selectOne(Wrappers.lambdaQuery(PhoneCode.class).eq(PhoneCode::getPhone, phone).last("limit 1"));
+            if (phoneCode == null) {
+                phoneCode = new PhoneCode();
+                phoneCode.setPhone(phone);
+            }
+            phoneCode.setCode(code);
+            if (phoneCode.getId() == null) {
+                phoneCode.setCid(cid);
+                phoneCodeMapper.insert(phoneCode);
+            } else {
+                phoneCodeMapper.updateById(phoneCode);
+            }
+        } catch (DuplicateKeyException e) {
         }
         return GatewayResponse.SUCCESS.newBuilder().toResult(
                 "发送成功");
@@ -375,6 +388,9 @@ public class AuthorizationController {
         //写回登陆信息
         StpUserUtil.login(user.getId());
         LoginPhoneRep loginPhoneRep = new LoginPhoneRep(StpUserUtil.getTokenValue(), user.getShowId(), user.getNickname(), user.getHeadimgurl());
+        if (loginReq.getPhone() != null) {
+            redisService.del(RedisService.key.PHONE_CODE_KEY.getName() + loginReq.getPhone());
+        }
         return GatewayResponse.SUCCESS.newBuilder().toResult(loginPhoneRep);
     }