|
|
@@ -0,0 +1,171 @@
|
|
|
+package com.fs.his.utils;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.fs.common.utils.StringUtils;
|
|
|
+
|
|
|
+import java.lang.reflect.Field;
|
|
|
+import java.lang.reflect.Modifier;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 递归脱敏响应中的手机号字段(含嵌套 JSON 字符串内的 mobile/phone)。
|
|
|
+ */
|
|
|
+public final class PhoneMaskHelper {
|
|
|
+
|
|
|
+ private static final Set<String> PHONE_FIELDS = new HashSet<>(Arrays.asList(
|
|
|
+ "phone", "mobile", "userphone", "patientphone", "phonenumber",
|
|
|
+ "phoneNumber", "userPhone", "patientPhone", "patientMobile", "contactPhone",
|
|
|
+ "receivePhone", "receiverPhone", "tel", "telephone", "phoneMk"
|
|
|
+ ));
|
|
|
+
|
|
|
+ private PhoneMaskHelper() {
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Object maskObject(Object body) {
|
|
|
+ if (body == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ maskRecursive(body, Collections.newSetFromMap(new IdentityHashMap<>()), 0);
|
|
|
+ return body;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void maskRecursive(Object obj, Set<Object> visited, int depth) {
|
|
|
+ if (obj == null || depth > 12) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ Class<?> clazz = obj.getClass();
|
|
|
+ if (clazz.isPrimitive() || obj instanceof Number || obj instanceof Boolean
|
|
|
+ || obj instanceof CharSequence || obj instanceof Enum
|
|
|
+ || obj instanceof Date || obj instanceof byte[] || obj instanceof Class) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (visited.contains(obj)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // JDK / 第三方不可写集合等跳过深挖风险对象
|
|
|
+ String cn = clazz.getName();
|
|
|
+ if (cn.startsWith("java.") && !(obj instanceof Map || obj instanceof Collection)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ visited.add(obj);
|
|
|
+
|
|
|
+ if (obj instanceof Map) {
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ Map<Object, Object> map = (Map<Object, Object>) obj;
|
|
|
+ for (Map.Entry<Object, Object> e : map.entrySet()) {
|
|
|
+ Object key = e.getKey();
|
|
|
+ Object val = e.getValue();
|
|
|
+ if (key != null && isPhoneField(String.valueOf(key)) && val instanceof String) {
|
|
|
+ e.setValue(PhoneUtil.maskForResponse((String) val));
|
|
|
+ } else if (val instanceof String && isPhoneJsonField(String.valueOf(key))) {
|
|
|
+ e.setValue(maskJsonString((String) val));
|
|
|
+ } else {
|
|
|
+ maskRecursive(val, visited, depth + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (obj instanceof Collection) {
|
|
|
+ for (Object item : (Collection<?>) obj) {
|
|
|
+ maskRecursive(item, visited, depth + 1);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (obj instanceof Object[]) {
|
|
|
+ for (Object item : (Object[]) obj) {
|
|
|
+ maskRecursive(item, visited, depth + 1);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (Field field : getAllFields(clazz)) {
|
|
|
+ if (Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ field.setAccessible(true);
|
|
|
+ Object val = field.get(obj);
|
|
|
+ if (val == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ String name = field.getName();
|
|
|
+ if (val instanceof String) {
|
|
|
+ if (isPhoneField(name)) {
|
|
|
+ field.set(obj, PhoneUtil.maskForResponse((String) val));
|
|
|
+ } else if (isPhoneJsonField(name)) {
|
|
|
+ field.set(obj, maskJsonString((String) val));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ maskRecursive(val, visited, depth + 1);
|
|
|
+ }
|
|
|
+ } catch (Throwable ignored) {
|
|
|
+ // 忽略不可写/模块限制字段
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static boolean isPhoneField(String name) {
|
|
|
+ if (StringUtils.isEmpty(name)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ String n = name.toLowerCase(Locale.ROOT);
|
|
|
+ if (PHONE_FIELDS.contains(name) || PHONE_FIELDS.contains(n)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return n.endsWith("phone") || n.endsWith("mobile") || "phonenumber".equals(n);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static boolean isPhoneJsonField(String name) {
|
|
|
+ if (StringUtils.isEmpty(name)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ String n = name.toLowerCase(Locale.ROOT);
|
|
|
+ return "patientjson".equals(n) || "userjson".equals(n) || n.endsWith("json");
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String maskJsonString(String json) {
|
|
|
+ if (StringUtils.isEmpty(json) || (!json.trim().startsWith("{") && !json.trim().startsWith("["))) {
|
|
|
+ return json;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ Object parsed = JSON.parse(json);
|
|
|
+ maskFastjson(parsed);
|
|
|
+ return JSON.toJSONString(parsed);
|
|
|
+ } catch (Exception e) {
|
|
|
+ return json;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void maskFastjson(Object node) {
|
|
|
+ if (node instanceof JSONObject) {
|
|
|
+ JSONObject jo = (JSONObject) node;
|
|
|
+ for (String key : new ArrayList<>(jo.keySet())) {
|
|
|
+ Object val = jo.get(key);
|
|
|
+ if (val instanceof String && isPhoneField(key)) {
|
|
|
+ jo.put(key, PhoneUtil.maskForResponse((String) val));
|
|
|
+ } else {
|
|
|
+ maskFastjson(val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (node instanceof JSONArray) {
|
|
|
+ JSONArray arr = (JSONArray) node;
|
|
|
+ for (int i = 0; i < arr.size(); i++) {
|
|
|
+ maskFastjson(arr.get(i));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static List<Field> getAllFields(Class<?> clazz) {
|
|
|
+ List<Field> fields = new ArrayList<>();
|
|
|
+ Class<?> c = clazz;
|
|
|
+ while (c != null && c != Object.class) {
|
|
|
+ fields.addAll(Arrays.asList(c.getDeclaredFields()));
|
|
|
+ c = c.getSuperclass();
|
|
|
+ }
|
|
|
+ return fields;
|
|
|
+ }
|
|
|
+}
|