|
@@ -5,6 +5,7 @@ import cn.hutool.http.HttpRequest;
|
|
|
import cn.hutool.http.HttpResponse;
|
|
import cn.hutool.http.HttpResponse;
|
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
|
+import com.fs.common.core.redis.RedisCache;
|
|
|
import com.fs.common.utils.DateUtils;
|
|
import com.fs.common.utils.DateUtils;
|
|
|
import com.fs.common.utils.StringUtils;
|
|
import com.fs.common.utils.StringUtils;
|
|
|
import com.fs.his.config.StoreConfig;
|
|
import com.fs.his.config.StoreConfig;
|
|
@@ -15,6 +16,7 @@ import org.springframework.stereotype.Service;
|
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.nio.charset.StandardCharsets;
|
|
|
import java.text.SimpleDateFormat;
|
|
import java.text.SimpleDateFormat;
|
|
|
import java.util.Date;
|
|
import java.util.Date;
|
|
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 三方身份验证
|
|
* 三方身份验证
|
|
@@ -23,7 +25,6 @@ import java.util.Date;
|
|
|
public class IdCardUtil {
|
|
public class IdCardUtil {
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private static ISysConfigService configService;
|
|
private static ISysConfigService configService;
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
* 身份证信息隐藏 中间
|
|
* 身份证信息隐藏 中间
|
|
|
* @param idCard 身份证信息
|
|
* @param idCard 身份证信息
|
|
@@ -160,4 +161,73 @@ public class IdCardUtil {
|
|
|
throw new IllegalArgumentException("身份证出生日期解析失败", e);
|
|
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("身份证校验错误");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|