cloud-server-account/src/main/java/com/dsh/privacy/SensitiveDataMaskingAdvice.java
@@ -7,6 +7,9 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -22,6 +25,11 @@ @ControllerAdvice public class SensitiveDataMaskingAdvice implements ResponseBodyAdvice<Object> { private static final String INTERNAL_SENSITIVE_ACCESS = "X-Internal-Sensitive-Access"; /** APP endpoints whose clients receive original values and mask only when displaying them. */ private static final Set<String> APP_UNMASKED_PATHS = new HashSet<String>(Arrays.asList( "/base/exploreWP/storeDetail", "/base/exploreWP/customerList" )); private final HttpServletRequest request; @@ -31,9 +39,13 @@ @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { // The only unmasked account response is the management-only endpoint. return !(request.getRequestURI().startsWith("/internal/sensitive/") && "true".equalsIgnoreCase(request.getHeader(INTERNAL_SENSITIVE_ACCESS))); String path = request.getRequestURI(); // Internal management calls require their sensitive-access header. The // two explicitly listed APP endpoints return original values for // client-side display masking; all other account responses are masked. return !(APP_UNMASKED_PATHS.contains(path) || (path.startsWith("/internal/sensitive/") && "true".equalsIgnoreCase(request.getHeader(INTERNAL_SENSITIVE_ACCESS)))); } @Override cloud-server-gateway/src/main/java/cn/mb/cloud/gateway/filter/SensitiveDataMaskingGlobalFilter.java
@@ -15,6 +15,9 @@ import reactor.core.publisher.Mono; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -23,12 +26,28 @@ public class SensitiveDataMaskingGlobalFilter implements GlobalFilter, Ordered { private static final Pattern FIELD = Pattern.compile("(?i)(\\\"(?:[a-z]*phone|[a-z]*mobile|telephone|tel)\\\"\\s*:\\s*\\\")([^\\\"]*)(\\\")"); private static final Pattern IDCARD = Pattern.compile("(?i)(\\\"(?:[a-z]*idcard|[a-z]*identitycard|identitynumber|idnumber)\\\"\\s*:\\s*\\\")([^\\\"]*)(\\\")"); /** * APP has to receive these values so it can apply its own display masking. * Keep this list exact: all other routes continue to be protected at the * gateway boundary. */ private static final Set<String> APP_UNMASKED_PATHS = new HashSet<String>(Arrays.asList( "/other/api/site/queryMySiteById", "/account/base/exploreWP/storeDetail", "/other/base/store/queryStoreFreeBenefit", "/other/base/site/querySiteInfo", "/activity/huimin/getStudentListByCarId", "/account/base/exploreWP/customerList" )); @Override public int getOrder() { return -2; } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // This route itself enforces the admin check and writes an audit record. if (exchange.getRequest().getURI().getPath().startsWith("/system/sensitive/")) return chain.filter(exchange); String path = exchange.getRequest().getURI().getPath(); // /system/sensitive enforces its own admin check and audit. The APP // paths below deliberately return their original fields for client-side // display masking; every other response remains gateway-masked. if (path.startsWith("/system/sensitive/") || APP_UNMASKED_PATHS.contains(path)) return chain.filter(exchange); ServerHttpResponseDecorator decorated = new ServerHttpResponseDecorator(exchange.getResponse()) { @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { MediaType type = getHeaders().getContentType(); cloud-server-other/src/main/java/com/dsh/other/controller/SiteController.java
@@ -677,9 +677,18 @@ }) public ResultUtil<Map<String, Object>> queryMySiteById(Integer id) { try { Integer uid = tokenUtil.getUserIdFormRedis(); if (uid == null) { return ResultUtil.tokenErr(); } HashMap<String, Object> map = new HashMap<>(); System.err.println("预约id"+id); SiteBooking siteBooking = siteBookingService.getById(id); // This endpoint now returns the original contact data to the APP, // so an authenticated user may only read their own booking. if (siteBooking == null || !uid.equals(siteBooking.getAppUserId())) { return ResultUtil.error("预约记录不存在"); } Integer targetAudience = siteBooking.getTargetAudience(); System.err.println("预约"+siteBooking); Integer storeId = siteBooking.getStoreId(); cloud-server-other/src/main/java/com/dsh/other/controller/TVipController.java
@@ -60,6 +60,10 @@ for (Vip vip : list) { List<CouponVipResp> couponVipRespList = new ArrayList<>(); List<TicketVipResp> ticketVipRespList = new ArrayList<>(); // The APP contract requires arrays even when this membership card // has no coupon or ticket benefits. vip.setCouponList(couponVipRespList); vip.setTicketList(ticketVipRespList); String couponJson = vip.getCouponJson(); JSONArray couponJsonArray = JSONArray.parseArray(couponJson); if(couponJsonArray==null){