فهرست منبع

update:身份校验优化 防止恶意调用

ct 2 هفته پیش
والد
کامیت
af1e470f1c

+ 71 - 1
fs-service/src/main/java/com/fs/his/utils/IdCardUtil.java

@@ -5,6 +5,7 @@ import cn.hutool.http.HttpRequest;
 import cn.hutool.http.HttpResponse;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.his.config.StoreConfig;
@@ -15,6 +16,7 @@ import org.springframework.stereotype.Service;
 import java.nio.charset.StandardCharsets;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.concurrent.TimeUnit;
 
 /**
  * 三方身份验证
@@ -23,7 +25,6 @@ import java.util.Date;
 public class IdCardUtil {
     @Autowired
     private static ISysConfigService configService;
-
     /**
      * 身份证信息隐藏 中间
      * @param idCard   身份证信息
@@ -160,4 +161,73 @@ public class IdCardUtil {
             throw new IllegalArgumentException("身份证出生日期解析失败", e);
         }
     }
+
+    /** 配置参数 */
+    private static final int USER_LIMIT_PER_MIN = 5; //同一个登录用户,1 分钟内最多允许发起 5 次身份证校验
+    private static final int ID_LIMIT_PER_10_MIN = 3; //同一个身份证号,10 分钟内最多允许被校验 3 次(不管是谁调)
+    private static final int FAIL_LIMIT = 10; //同一个用户,在 10 分钟内最多允许 3 次“校验失败”
+    public static void verify(
+                       RedisCache redisCache,
+                       StoreConfig storeConfig,
+                       String userId,
+                       String patientName,
+                       String idCard,
+                       String mobile) {
+
+        // ========== 0 失败次数封禁(必须前置) ==========
+        String failKey = "ID_VERIFY:FAIL:" + userId;
+        Integer failCountCache = redisCache.getCacheObject(failKey);
+        if (failCountCache != null && failCountCache >= FAIL_LIMIT) {
+            throw new RuntimeException("身份证校验失败次数过多,请10分钟后再试");
+        }
+
+        // ========== 1. 用户频率限制 ==========
+        String userLimitKey = "ID_VERIFY:USER:" + userId;
+        Long userCount = redisCache.incr(userLimitKey, 1);
+        // 第一次设置过期
+        if (userCount != null && userCount == 1) {
+            redisCache.expire(userLimitKey, 1, TimeUnit.MINUTES);
+        }
+
+        if (userCount != null && userCount > USER_LIMIT_PER_MIN) {
+            throw new RuntimeException("操作过于频繁,请稍后再试");
+        }
+
+        // ========== 2. 身份证频率限制 ==========
+        String idKey = "ID_VERIFY:IDCARD:" + idCard;
+        Long idCount = redisCache.incr(idKey, 1);
+        if (idCount != null && idCount == 1) {
+            redisCache.expire(idKey, 10, TimeUnit.MINUTES);
+        }
+        if (idCount != null && idCount > ID_LIMIT_PER_10_MIN) {
+            throw new RuntimeException("该身份证校验次数过多,请稍后再试");
+        }
+
+        // ========== 3. 校验结果缓存 ==========
+        String resultKey = "ID_VERIFY:RESULT:" + patientName + ":" + idCard;
+        Boolean cacheResult = redisCache.getCacheObject(resultKey);
+        if (cacheResult != null) {
+            if (!cacheResult) {
+                throw new RuntimeException("身份证校验错误");
+            }
+            return;
+        }
+
+        // ========== 4. 调用三方接口 ==========
+        boolean match = idCard.length() == 18
+                ? IdCardUtil.isMatchById(storeConfig, patientName, idCard)
+                : IdCardUtil.isMatchByMobile(storeConfig, patientName, mobile);
+
+        // ========== 5. 缓存结果 ==========
+        redisCache.setCacheObject(resultKey, match, 1, TimeUnit.DAYS);
+
+        // ========== 6. 失败处理 ==========
+        if (!match) {
+            Long failCount = redisCache.incr(failKey, 1);
+            if (failCount != null && failCount == 1) {
+                redisCache.expire(failKey, 10, TimeUnit.MINUTES);
+            }
+            throw new RuntimeException("身份证校验错误");
+        }
+    }
 }

+ 8 - 17
fs-user-app/src/main/java/com/fs/app/controller/PatientController.java

@@ -158,14 +158,10 @@ public class PatientController extends  AppBaseController {
 
         //三方校验
         if (isIdVerification!=null&&isIdVerification == 1){
-            boolean match = true;
-            if (idCardNumber.length() != 18) {
-                match = IdCardUtil.isMatchByMobile(storeConfig,param.getPatientName(), mobile);
-            } else {
-                match = IdCardUtil.isMatchById(storeConfig,param.getPatientName(), param.getIdCard());
-            }
-            if (!match){
-                return R.error("身份证校验错误");
+            try {
+                IdCardUtil.verify(redisCache,storeConfig,getUserId(),param.getPatientName(),param.getIdCard(),mobile);
+            } catch (Exception e) {
+                return R.error(e.getMessage());
             }
         }
 
@@ -256,17 +252,12 @@ public class PatientController extends  AppBaseController {
 
         //三方校验
         if (isIdVerification!=null&&isIdVerification == 1){
-            boolean match = true;
-            if (idCardNumber.length() != 18) {
-                match = IdCardUtil.isMatchByMobile(storeConfig,param.getPatientName(), mobile);
-            } else {
-                match = IdCardUtil.isMatchById(storeConfig,param.getPatientName(), param.getIdCard());
-            }
-            if (!match){
-                return R.error("身份证校验错误");
+            try {
+                IdCardUtil.verify(redisCache,storeConfig,getUserId(),param.getPatientName(),param.getIdCard(),mobile);
+            } catch (Exception e) {
+                return R.error(e.getMessage());
             }
         }
-
         FsPatient patient=new FsPatient();
         BeanUtil.copyProperties(param, patient);
         patientService.updateFsPatient(patient);