소스 검색

1、检测安全风险并且进行处理

yys 2 일 전
부모
커밋
9ebeff2de9
40개의 변경된 파일650개의 추가작업 그리고 133개의 파일을 삭제
  1. 1 0
      fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java
  2. 2 0
      fs-admin/src/main/java/com/fs/his/controller/FsUserController.java
  3. 13 0
      fs-common/src/main/java/com/fs/common/annotation/SkipPhoneMask.java
  4. 68 0
      fs-common/src/main/java/com/fs/common/utils/security/SafeRemoteUrlUtils.java
  5. 2 0
      fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java
  6. 5 3
      fs-company-app/src/main/java/com/fs/app/controller/UserController.java
  7. 0 7
      fs-company-app/src/main/java/com/fs/app/interceptor/AuthorizationInterceptor.java
  8. 4 7
      fs-company/src/main/java/com/fs/company/controller/store/FsInquiryOrderController.java
  9. 3 12
      fs-company/src/main/java/com/fs/company/controller/store/FsInquiryOrderReportController.java
  10. 1 1
      fs-company/src/main/java/com/fs/company/controller/store/FsStoreOrderController.java
  11. 9 2
      fs-company/src/main/java/com/fs/company/controller/store/FsUserController.java
  12. 1 1
      fs-company/src/main/java/com/fs/hisStore/controller/FsIntegralOrderController.java
  13. 3 0
      fs-company/src/main/java/com/fs/user/FsUserAdminController.java
  14. 1 1
      fs-doctor-app/src/main/java/com/fs/app/config/WebMvcConfig.java
  15. 19 2
      fs-doctor-app/src/main/java/com/fs/app/controller/AppBaseController.java
  16. 13 1
      fs-doctor-app/src/main/java/com/fs/app/controller/DiagnosisController.java
  17. 12 15
      fs-doctor-app/src/main/java/com/fs/app/controller/DoctorWordsController.java
  18. 26 1
      fs-doctor-app/src/main/java/com/fs/app/controller/DrugReportController.java
  19. 14 2
      fs-doctor-app/src/main/java/com/fs/app/controller/FollowController.java
  20. 8 3
      fs-doctor-app/src/main/java/com/fs/app/controller/FsUserInformationCollectionController.java
  21. 16 8
      fs-doctor-app/src/main/java/com/fs/app/controller/InquiryOrderController.java
  22. 34 11
      fs-doctor-app/src/main/java/com/fs/app/controller/PatientController.java
  23. 13 0
      fs-doctor-app/src/main/java/com/fs/app/controller/PrescribeController.java
  24. 7 0
      fs-doctor-app/src/main/java/com/fs/app/controller/StoreOrderController.java
  25. 82 0
      fs-service/src/main/java/com/fs/framework/web/advice/PhoneMaskResponseBodyAdvice.java
  26. 171 0
      fs-service/src/main/java/com/fs/his/utils/PhoneMaskHelper.java
  27. 29 0
      fs-service/src/main/java/com/fs/his/utils/PhoneUtil.java
  28. 5 0
      fs-service/src/main/resources/application-common.yml
  29. 2 0
      fs-user-app/src/main/java/com/fs/app/controller/AppLoginController.java
  30. 2 13
      fs-user-app/src/main/java/com/fs/app/controller/CommonController.java
  31. 35 4
      fs-user-app/src/main/java/com/fs/app/controller/CompanyUserController.java
  32. 10 2
      fs-user-app/src/main/java/com/fs/app/controller/TalentController.java
  33. 1 0
      fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserController.java
  34. 2 0
      fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserLoginController.java
  35. 6 1
      fs-user-app/src/main/java/com/fs/app/controller/game/PlayerController.java
  36. 8 3
      fs-user-app/src/main/java/com/fs/app/controller/store/AppLoginScrmController.java
  37. 15 4
      fs-user-app/src/main/java/com/fs/app/controller/store/CompanyUserScrmController.java
  38. 2 5
      fs-user-app/src/main/java/com/fs/app/controller/store/CourseScrmController.java
  39. 0 7
      fs-user-app/src/main/java/com/fs/app/interceptor/AuthorizationInterceptor.java
  40. 5 17
      fs-user-app/src/main/java/com/fs/framework/aspectj/UserOperationLogAspect.java

+ 1 - 0
fs-admin/src/main/java/com/fs/his/controller/FsStoreOrderController.java

@@ -396,6 +396,7 @@ public class FsStoreOrderController extends BaseController
     /**
      * 获取订单详细信息
      */
+    @PreAuthorize("@ss.hasPermi('his:storeOrder:list') or @ss.hasPermi('store:storeOrder:list') or @ss.hasPermi('his:storeOrder:export')")
     @GetMapping(value = "/{orderId}")
     public R getInfo(@PathVariable("orderId") Long orderId) throws ParseException {
         FsStoreOrderVO order = fsStoreOrderService.selectFsStoreOrderByOrderIdVO(orderId);

+ 2 - 0
fs-admin/src/main/java/com/fs/his/controller/FsUserController.java

@@ -249,6 +249,7 @@ public class FsUserController extends BaseController
     /**
      * 获取用户详细信息
      */
+    @PreAuthorize("@ss.hasPermi('his:user:query') or @ss.hasPermi('his:user:list')")
     @GetMapping(value = "/{userId}")
     public AjaxResult getInfo(@PathVariable("userId") Long userId)
     {
@@ -257,6 +258,7 @@ public class FsUserController extends BaseController
         return AjaxResult.success(fsUser);
     }
 
+    @PreAuthorize("@ss.hasPermi('his:user:query') or @ss.hasPermi('his:user:list')")
     @GetMapping(value = "/getUserAddr/{userId}")
     public AjaxResult getUserAddr(@PathVariable("userId") Long userId)
     {

+ 13 - 0
fs-common/src/main/java/com/fs/common/annotation/SkipPhoneMask.java

@@ -0,0 +1,13 @@
+package com.fs.common.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 标记接口允许返回完整手机号(需业务强需求 + 权限控制)。
+ * 默认所有响应会做手机号脱敏。
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface SkipPhoneMask {
+}

+ 68 - 0
fs-common/src/main/java/com/fs/common/utils/security/SafeRemoteUrlUtils.java

@@ -0,0 +1,68 @@
+package com.fs.common.utils.security;
+
+import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.StringUtils;
+
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * 远程 URL 安全校验,防止 SSRF。
+ * 策略偏保守:只拦协议/主机黑名单与字面量私网 IP,不对域名做强制 DNS 解析拦截,避免误伤公网 OSS/CDN。
+ */
+public final class SafeRemoteUrlUtils {
+
+    private static final Set<String> ALLOWED_SCHEMES = new HashSet<>(Arrays.asList("http", "https"));
+    private static final Pattern IPV4 = Pattern.compile("^\\d{1,3}(\\.\\d{1,3}){3}$");
+
+    private SafeRemoteUrlUtils() {
+    }
+
+    public static void validateHttpUrl(String rawUrl) {
+        if (StringUtils.isEmpty(rawUrl)) {
+            throw new ServiceException("URL不能为空");
+        }
+        final URI uri;
+        try {
+            uri = new URI(rawUrl.trim());
+        } catch (Exception e) {
+            throw new ServiceException("非法URL");
+        }
+        String scheme = uri.getScheme() == null ? "" : uri.getScheme().toLowerCase(Locale.ROOT);
+        if (!ALLOWED_SCHEMES.contains(scheme)) {
+            throw new ServiceException("仅支持http/https协议");
+        }
+        String host = uri.getHost();
+        if (StringUtils.isEmpty(host)) {
+            throw new ServiceException("非法URL主机");
+        }
+        String hostLower = host.toLowerCase(Locale.ROOT);
+        if ("localhost".equals(hostLower) || hostLower.endsWith(".localhost")
+                || "metadata.google.internal".equals(hostLower)
+                || "metadata".equals(hostLower)
+                || "metadata.tencentyun.com".equals(hostLower)
+                || "169.254.169.254".equals(hostLower)) {
+            throw new ServiceException("禁止访问内网或元数据地址");
+        }
+        if (IPV4.matcher(hostLower).matches()) {
+            try {
+                InetAddress address = InetAddress.getByName(hostLower);
+                if (address.isAnyLocalAddress() || address.isLoopbackAddress()
+                        || address.isLinkLocalAddress() || address.isSiteLocalAddress()
+                        || address.isMulticastAddress()) {
+                    throw new ServiceException("禁止访问内网地址");
+                }
+            } catch (ServiceException se) {
+                throw se;
+            } catch (UnknownHostException ignored) {
+                // ignore
+            }
+        }
+    }
+}

+ 2 - 0
fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java

@@ -17,6 +17,7 @@ import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.PatternUtils;
 import com.fs.common.utils.bean.BeanUtils;
+import com.fs.common.utils.security.SafeRemoteUrlUtils;
 import com.fs.company.domain.*;
 import com.fs.company.mapper.CompanyRoleMapper;
 import com.fs.company.mapper.CompanyUserMapper;
@@ -492,6 +493,7 @@ public class CompanyUserController extends AppBaseController {
         companyUser.setVoicePrintUrl(param.getVoicePrintUrl());
 
         //转换音频格式 mp3-wav
+        SafeRemoteUrlUtils.validateHttpUrl(param.getVoicePrintUrl());
         String s = AudioUtils.audioWAVFromUrl(param.getVoicePrintUrl());
 
         //保存文件并且上传存储桶

+ 5 - 3
fs-company-app/src/main/java/com/fs/app/controller/UserController.java

@@ -23,7 +23,7 @@ import com.fs.common.utils.PinYinUtil;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.bean.BeanUtils;
 import com.fs.common.utils.http.HttpUtils;
-import com.fs.common.utils.sign.Md5Utils;
+import com.fs.common.utils.uuid.IdUtils;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.domain.CompanyUserCard;
@@ -211,11 +211,13 @@ public class UserController extends AppBaseController {
 
             //redisCache.setCacheObject("perms:"+companyUser.getUserId(), JSONUtil.toJsonStr(perms),2592000, TimeUnit.SECONDS);
             redisCache.setCacheObject("perms:" + companyUser.getUserId(), JSONUtil.toJsonStr(perms), 604800, TimeUnit.SECONDS);
-            redisCache.setCacheObject("company-user-token:" + Md5Utils.hash(companyUser.getUserId().toString()), companyUser.getUserId(), 100, TimeUnit.DAYS);
+            String companyUserToken = IdUtils.fastSimpleUUID();
+            Long uid = companyUser.getUserId();
+            redisCache.setCacheObject("company-user-token:" + companyUserToken, uid, 100, TimeUnit.DAYS);
             Map<String, Object> result = new HashMap<>();
             result.put("token", token);
             result.put("user", companyUser);
-            result.put("companyUserToken", Md5Utils.hash(companyUser.getUserId().toString()));
+            result.put("companyUserToken", companyUserToken);
             result.put("perms", perms);
             return R.ok("登录成功").put("data", result);
         } catch (Exception e) {

+ 0 - 7
fs-company-app/src/main/java/com/fs/app/interceptor/AuthorizationInterceptor.java

@@ -26,7 +26,6 @@ public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
     @Autowired
     RedisCache redisCache;
     public static final String USER_KEY = "userId";
-    private static final String SKIP_AUTH_HEADER = "X-Skip-Auth";
 
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
@@ -41,12 +40,6 @@ public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
             return true;
         }
 
-        // 请求头存在跳过认证标识,直接放行
-        String skipAuth = request.getHeader(SKIP_AUTH_HEADER);
-        if (StringUtils.isNotEmpty(skipAuth)) {
-            return true;
-        }
-
         //获取用户凭证
         String token = request.getHeader(jwtUtils.getHeader());
         if(StringUtils.isBlank(token)){

+ 4 - 7
fs-company/src/main/java/com/fs/company/controller/store/FsInquiryOrderController.java

@@ -224,7 +224,7 @@ public class FsInquiryOrderController extends BaseController
     /**
      * 获取问诊订单详细信息
      */
-
+    @PreAuthorize("@ss.hasPermi('store:inquiryOrder:list') or @ss.hasPermi('store:inquiryOrder:myList')")
     @GetMapping(value = "/{orderId}")
     public AjaxResult getInfo(@PathVariable("orderId") Long orderId)
     {
@@ -239,12 +239,7 @@ public class FsInquiryOrderController extends BaseController
             }
             String mobile = parse.get("mobile");
             if (mobile!=null){
-                if (mobile.length()>11){
-                    parse.put("mobile",PhoneUtil.decryptPhoneMk(mobile));
-                }else {
-                    parse.put("mobile",mobile.replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
-                }
-
+                parse.put("mobile", PhoneUtil.maskForResponse(mobile));
             }
 
             String s = JSON.toJSONString(parse);
@@ -349,6 +344,7 @@ public class FsInquiryOrderController extends BaseController
     /**
      * 查询订单log列表
      */
+    @PreAuthorize("@ss.hasPermi('store:inquiryOrder:list') or @ss.hasPermi('store:inquiryOrder:myList')")
     @GetMapping("/logList/{orderId}")
     public TableDataInfo logList(@PathVariable("orderId") String orderId)
     {
@@ -356,6 +352,7 @@ public class FsInquiryOrderController extends BaseController
         return getDataTable(list);
     }
 
+    @PreAuthorize("@ss.hasPermi('store:inquiryOrder:list') or @ss.hasPermi('store:inquiryOrder:myList')")
     @GetMapping(value = "/doctor/{doctorId}")
     public AjaxResult doctor(@PathVariable("doctorId") Long doctorId)
     {

+ 3 - 12
fs-company/src/main/java/com/fs/company/controller/store/FsInquiryOrderReportController.java

@@ -102,6 +102,7 @@ public class FsInquiryOrderReportController extends BaseController
     /**
      * 获取问诊报告详细信息
      */
+    @PreAuthorize("@ss.hasPermi('store:inquiryOrder:list') or @ss.hasPermi('store:inquiryOrder:myList')")
     @GetMapping(value = "/{reportId}")
     public AjaxResult getInfo(@PathVariable("reportId") Long reportId)
     {
@@ -116,23 +117,13 @@ public class FsInquiryOrderReportController extends BaseController
             }
             String mobile = parse.get("mobile");
             if (mobile!=null){
-                if (mobile.length()>11){
-                    parse.put("mobile", PhoneUtil.decryptPhoneMk(mobile));
-                }else {
-                    parse.put("mobile",mobile.replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
-                }
-
+                parse.put("mobile", PhoneUtil.maskForResponse(mobile));
             }
             String s = JSON.toJSONString(parse);
             fsInquiryOrderReportVO.setPatientJson(s);
         }
         if (fsInquiryOrderReportVO.getPhone()!=null){
-            if (fsInquiryOrderReportVO.getPhone().length()>11){
-                fsInquiryOrderReportVO.setPhone(PhoneUtil.decryptPhoneMk(fsInquiryOrderReportVO.getPhone()));
-            }else {
-                fsInquiryOrderReportVO.setPhone(fsInquiryOrderReportVO.getPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
-            }
-
+            fsInquiryOrderReportVO.setPhone(PhoneUtil.maskForResponse(fsInquiryOrderReportVO.getPhone()));
         }
         return AjaxResult.success(fsInquiryOrderReportVO);
     }

+ 1 - 1
fs-company/src/main/java/com/fs/company/controller/store/FsStoreOrderController.java

@@ -199,7 +199,7 @@ public class FsStoreOrderController extends BaseController
     /**
      * 获取订单详细信息
      */
-
+    @PreAuthorize("@ss.hasPermi('his:storeOrder:list') or @ss.hasPermi('his:storeOrder:myList')")
     @GetMapping(value = "/{orderId}")
     public AjaxResult getInfo(@PathVariable("orderId") Long orderId)
     {

+ 9 - 2
fs-company/src/main/java/com/fs/company/controller/store/FsUserController.java

@@ -77,6 +77,7 @@ public class FsUserController extends BaseController
         }
         return getDataTable(list);
     }
+    @PreAuthorize("@ss.hasPermi('his:user:list')")
     @GetMapping("/getUserList")
     public R getUserList( FsUser fsUser)
     {
@@ -89,6 +90,11 @@ public class FsUserController extends BaseController
             }
             list = fsUserService.selectFsUserList(fsUser);
         }
+        for (FsUser u : list) {
+            if (u.getPhone() != null) {
+                u.setPhone(decryptAutoPhoneMk(u.getPhone()));
+            }
+        }
         return R.ok().put("data", list);
     }
 
@@ -183,6 +189,7 @@ public class FsUserController extends BaseController
     /**
      * 获取用户详细信息
      */
+    @PreAuthorize("@ss.hasPermi('his:user:list')")
     @GetMapping(value = "/{userId}")
     public AjaxResult getInfo(@PathVariable("userId") Long userId)
     {
@@ -195,7 +202,7 @@ public class FsUserController extends BaseController
     /**
      * 获取用户详细信息
      */
-
+    @PreAuthorize("@ss.hasPermi('his:user:list')")
     @GetMapping(value = "/getUserAddr/{userId}")
     public AjaxResult getUserAddr(@PathVariable("userId") Long userId)
     {
@@ -203,7 +210,7 @@ public class FsUserController extends BaseController
         for (FsUserAddress fsUserAddress : fsUserAddresses) {
             fsUserAddress.setPhone(decryptAutoPhoneMk(fsUserAddress.getPhone()));
         }
-        return AjaxResult.success();
+        return AjaxResult.success(fsUserAddresses);
     }
 
 

+ 1 - 1
fs-company/src/main/java/com/fs/hisStore/controller/FsIntegralOrderController.java

@@ -124,7 +124,7 @@ public class FsIntegralOrderController extends BaseController
     /**
      * 获取积分商品订单详细信息
      */
-//    @PreAuthorize("@ss.hasPermi('his:integralOrder:query')")
+    @PreAuthorize("@ss.hasPermi('his:integralOrder:query') or @ss.hasPermi('his:integralOrder:list')")
     @GetMapping(value = "/{orderId}")
     public AjaxResult getInfo(@PathVariable("orderId") Long orderId)
     {

+ 3 - 0
fs-company/src/main/java/com/fs/user/FsUserAdminController.java

@@ -159,6 +159,7 @@ public class FsUserAdminController extends BaseController {
     /**
      * 获取用户详细信息
      */
+    @PreAuthorize("@ss.hasPermi('user:fsUser:list')")
     @GetMapping(value = "/{userId}")
     public AjaxResult getInfo(@PathVariable("userId") Long userId)
     {
@@ -168,6 +169,7 @@ public class FsUserAdminController extends BaseController {
     /**
      * 获取项目用户详细信息
      */
+    @PreAuthorize("@ss.hasPermi('user:fsUser:list')")
     @GetMapping(value = "/member/{id}")
     public AjaxResult getMemberInfo(@PathVariable("id") Long id)
     {
@@ -200,6 +202,7 @@ public class FsUserAdminController extends BaseController {
 
 
     @ApiOperation("后台会员批量发送课程消息")
+    @PreAuthorize("@ss.hasPermi('user:fsUser:edit')")
     @PostMapping("/batchSendCourse")
     public OpenImResponseDTO batchSendCourse(@RequestBody BatchSendCourseDTO batchSendCourseDTO) throws JsonProcessingException {
         // 生成看课短链

+ 1 - 1
fs-doctor-app/src/main/java/com/fs/app/config/WebMvcConfig.java

@@ -19,7 +19,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
 
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
-        registry.addInterceptor(authorizationInterceptor).addPathPatterns("/app/**");
+        registry.addInterceptor(authorizationInterceptor).addPathPatterns("/app/**", "/user/collection/**");
     }
 //
 //    @Override

+ 19 - 2
fs-doctor-app/src/main/java/com/fs/app/controller/AppBaseController.java

@@ -1,11 +1,14 @@
 package com.fs.app.controller;
 
 
+import com.fs.app.exception.FSException;
 import com.fs.app.utils.JwtUtils;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
+import com.fs.his.domain.FsInquiryOrder;
 import io.jsonwebtoken.Claims;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
 
 
 public class AppBaseController {
@@ -18,9 +21,23 @@ public class AppBaseController {
 	{
 		String headValue =  ServletUtils.getRequest().getHeader("APPToken");
 		Claims claims=jwtUtils.getClaimByToken(headValue);
-		String doctorId = claims.getSubject().toString();
-		return doctorId;
+		if (claims == null || claims.getSubject() == null) {
+			throw new FSException("未登录", HttpStatus.UNAUTHORIZED.value());
+		}
+		return claims.getSubject().toString();
 	}
 
+	/**
+	 * 校验问诊订单是否归属当前医生(未接单可查看,已接单仅归属医生)。
+	 */
+	protected void assertInquiryOrderAccess(FsInquiryOrder order) {
+		if (order == null) {
+			throw new FSException("订单不存在", HttpStatus.NOT_FOUND.value());
+		}
+		Long doctorId = Long.parseLong(getDoctorId());
+		if (order.getDoctorId() != null && !order.getDoctorId().equals(doctorId)) {
+			throw new FSException("无权操作该订单", HttpStatus.FORBIDDEN.value());
+		}
+	}
 
 }

+ 13 - 1
fs-doctor-app/src/main/java/com/fs/app/controller/DiagnosisController.java

@@ -1,6 +1,8 @@
 package com.fs.app.controller;
 
+import com.fs.app.annotation.Login;
 import com.fs.common.core.domain.R;
+import com.fs.his.domain.FsFirstDiagnosis;
 import com.fs.his.param.FsDiagnosisFillDParam;
 import com.fs.his.param.FsDiagnosisListDParam;
 import com.fs.his.service.IFsFirstDiagnosisService;
@@ -19,6 +21,7 @@ public class DiagnosisController extends AppBaseController{
     @Autowired
     private IFsFirstDiagnosisService diagnosisService;
 
+    @Login
     @GetMapping("/getDiagnosisList")
     public R getDiagnosisList(FsDiagnosisListDParam param){
         param.setDoctorId(Long.parseLong(getDoctorId()));
@@ -28,14 +31,23 @@ public class DiagnosisController extends AppBaseController{
         return R.ok().put("data", pageInfo);
     }
 
+    @Login
     @PutMapping("/fill")
     public R fill(@RequestBody FsDiagnosisFillDParam param){
         param.setDoctorId(Long.parseLong(getDoctorId()));
         return diagnosisService.fill(param);
     }
 
+    @Login
     @GetMapping("/{id}")
     public R detail(@PathVariable("id") Long id){
-        return R.ok().put("data", diagnosisService.selectFsFirstDiagnosisById(id));
+        FsFirstDiagnosis diagnosis = diagnosisService.selectFsFirstDiagnosisById(id);
+        if (diagnosis == null) {
+            return R.error("记录不存在");
+        }
+        if (diagnosis.getDoctorId() != null && !diagnosis.getDoctorId().equals(Long.parseLong(getDoctorId()))) {
+            return R.error(403, "无权查看该诊断");
+        }
+        return R.ok().put("data", diagnosis);
     }
 }

+ 12 - 15
fs-doctor-app/src/main/java/com/fs/app/controller/DoctorWordsController.java

@@ -1,25 +1,15 @@
 package com.fs.app.controller;
 
-import com.fs.common.annotation.Log;
-import com.fs.common.core.controller.BaseController;
-import com.fs.common.core.domain.AjaxResult;
+import com.fs.app.annotation.Login;
 import com.fs.common.core.domain.R;
-import com.fs.common.core.page.TableDataInfo;
-import com.fs.common.enums.BusinessType;
-import com.fs.common.utils.poi.ExcelUtil;
-import com.fs.his.domain.FsDoctorProduct;
 import com.fs.his.domain.FsDoctorWords;
-import com.fs.his.param.FsDoctorArticleListUParam;
 import com.fs.his.param.FsDoctorWordsListUParam;
-import com.fs.his.service.IFsDoctorProductService;
 import com.fs.his.service.IFsDoctorWordsService;
-import com.fs.his.vo.FsDoctorArticleListUVO;
 import com.fs.his.vo.FsDoctorWordsListUVO;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
@@ -32,6 +22,7 @@ public class DoctorWordsController extends AppBaseController
     @Autowired
     private IFsDoctorWordsService doctorWordsService;
 
+    @Login
     @GetMapping("/getDoctorWordsList")
     public R getDoctorWordsList(FsDoctorWordsListUParam param)
     {
@@ -42,7 +33,7 @@ public class DoctorWordsController extends AppBaseController
         return R.ok().put("data",listPageInfo);
     }
 
-
+    @Login
     @PostMapping("/addDoctorWords")
     public R addDoctorWords(@RequestBody FsDoctorWords param)
     {
@@ -53,11 +44,19 @@ public class DoctorWordsController extends AppBaseController
         else{
             return R.error();
         }
-
     }
+
+    @Login
     @PostMapping("/delDoctorWords")
     public R delDoctorWords(@RequestBody FsDoctorWords param)
     {
+        FsDoctorWords exist = doctorWordsService.selectFsDoctorWordsById(param.getId());
+        if (exist == null) {
+            return R.error("常用语不存在");
+        }
+        if (exist.getDoctorId() != null && !exist.getDoctorId().equals(Long.parseLong(getDoctorId()))) {
+            return R.error(403, "无权删除");
+        }
         if(doctorWordsService.deleteFsDoctorWordsById(param.getId())>0){
             return R.ok();
         }
@@ -65,6 +64,4 @@ public class DoctorWordsController extends AppBaseController
             return R.error();
         }
     }
-
-
 }

+ 26 - 1
fs-doctor-app/src/main/java/com/fs/app/controller/DrugReportController.java

@@ -4,6 +4,7 @@ package com.fs.app.controller;
 import cn.hutool.json.JSONUtil;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fs.app.annotation.Login;
 import com.fs.app.param.DrugReportAddParam;
 import com.fs.app.param.DrugReportFinishParam;
 import com.fs.common.annotation.RepeatSubmit;
@@ -61,6 +62,7 @@ public class DrugReportController extends AppBaseController {
     private IFsDrugReportCountService fsDrugReportCountService;
     @Autowired
     private OpenIMService openIMService;
+    @Login
     @ApiOperation("获取报告列表")
     @GetMapping("/getDrugReportList")
     public R getDrugReportList(FsDrugReportListDParam param)
@@ -72,21 +74,36 @@ public class DrugReportController extends AppBaseController {
         return R.ok().put("data",listPageInfo);
     }
 
+    @Login
     @ApiOperation("获取详情")
     @GetMapping("/getDrugReportById")
     public R getDrugReportById(@RequestParam("reportId")Long reportId, HttpServletRequest request){
         FsDrugReportDVO reportDVO=drugReportService.selectFsDrugReportDVOByReportId(reportId);
+        if (reportDVO == null) {
+            return R.error("报告不存在");
+        }
+        if (reportDVO.getDoctorId() != null && !reportDVO.getDoctorId().equals(Long.parseLong(getDoctorId()))) {
+            return R.error(403, "无权查看该报告");
+        }
         return R.ok().put("data",reportDVO);
     }
 
+    @Login
     @ApiOperation("提交报告")
     @PostMapping("/addReport")
     public R addReport(@Validated @RequestBody DrugReportAddParam param, HttpServletRequest request) throws JsonProcessingException {
         FsFollow follow=followService.selectFsFollowByFollowId(param.getFollowId());
+        if (follow == null) {
+            return R.error("随访不存在");
+        }
+        Long doctorId = Long.parseLong(getDoctorId());
+        if (follow.getDoctorId() != null && !follow.getDoctorId().equals(doctorId)) {
+            return R.error(403, "无权操作该随访");
+        }
 
         FsDrugReport report=new FsDrugReport();
         BeanUtils.copyProperties(param,report);
-        report.setDoctorId(Long.parseLong(getDoctorId()));
+        report.setDoctorId(doctorId);
         report.setStatus(1);
         report.setStoreOrderId(follow.getStoreOrderId());
         report.setUserId(follow.getUserId());
@@ -127,11 +144,19 @@ public class DrugReportController extends AppBaseController {
         }
 
     }
+    @Login
     @ApiOperation("完成咨询")
     @PostMapping("/finishDrugReport")
     @RepeatSubmit
     public R finishDrugReport(@Validated @RequestBody DrugReportFinishParam param, HttpServletRequest request) throws JsonProcessingException {
         FsFollow follow=followService.selectFsFollowByFollowId(param.getFollowId());
+        if (follow == null) {
+            return R.error("随访不存在");
+        }
+        Long doctorId = Long.parseLong(getDoctorId());
+        if (follow.getDoctorId() != null && !follow.getDoctorId().equals(doctorId)) {
+            return R.error(403, "无权操作该随访");
+        }
         //发送给用户
         MsgDTO msgDTO=new MsgDTO();
         MsgCustomDTO customDTO=new MsgCustomDTO();

+ 14 - 2
fs-doctor-app/src/main/java/com/fs/app/controller/FollowController.java

@@ -39,6 +39,7 @@ public class FollowController extends AppBaseController {
 
     @Autowired
     private IFsUserService userService;
+    @Login
     @ApiOperation("获取随访列表")
     @GetMapping("/getFollowList")
     public R getFollowList(FsFollowListDParam param)
@@ -48,7 +49,7 @@ public class FollowController extends AppBaseController {
         List<FsFollowListDVO> list=followService.selectFsFollowListDVO(param);
         for (FsFollowListDVO fsFollowListDVO : list) {
             if(fsFollowListDVO.getPatientPhone()!=null&&!fsFollowListDVO.getPatientPhone().equals("")) {
-                fsFollowListDVO.setPatientPhone(fsFollowListDVO.getPatientPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+                fsFollowListDVO.setPatientPhone(com.fs.his.utils.PhoneUtil.maskForResponse(fsFollowListDVO.getPatientPhone()));
             }
 
         }
@@ -56,14 +57,25 @@ public class FollowController extends AppBaseController {
         return R.ok().put("data",listPageInfo);
     }
 
+    @Login
     @ApiOperation("获取随访详情")
     @GetMapping("/getFollowById")
     public R getFollowById(@RequestParam("followId")Long followId, HttpServletRequest request){
         FsFollow follow=followService.selectFsFollowByFollowId(followId);
+        if (follow == null) {
+            return R.error("随访不存在");
+        }
+        Long doctorId = Long.parseLong(getDoctorId());
+        if (follow.getDoctorId() != null && !follow.getDoctorId().equals(doctorId)) {
+            return R.error(403, "无权查看该随访");
+        }
         if(follow.getPatientPhone()!=null&&!follow.getPatientPhone().equals("")) {
-            follow.setPatientPhone(follow.getPatientPhone().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+            follow.setPatientPhone(com.fs.his.utils.PhoneUtil.maskForResponse(follow.getPatientPhone()));
         }
         FsUser user=userService.selectFsUserByUserId(follow.getUserId());
+        if (user != null && user.getPhone() != null) {
+            user.setPhone(com.fs.his.utils.PhoneUtil.maskForResponse(user.getPhone()));
+        }
         return R.ok().put("follow",follow).put("user",user);
     }
 

+ 8 - 3
fs-doctor-app/src/main/java/com/fs/app/controller/FsUserInformationCollectionController.java

@@ -1,5 +1,6 @@
 package com.fs.app.controller;
 
+import com.fs.app.annotation.Login;
 import com.fs.common.core.domain.R;
 import com.fs.his.domain.FsUserInformationCollection;
 import com.fs.his.param.UserInformationDoctorType2Param;
@@ -16,19 +17,23 @@ import java.util.List;
 public class FsUserInformationCollectionController extends  AppBaseController {
     @Autowired
     private IFsUserInformationCollectionService fsUserInformationCollectionService;
+
+    @Login
     @GetMapping("/getUserInformation")
     public R getUserInformation(@RequestParam("id") Long id) {
-
         return R.ok().put("data", fsUserInformationCollectionService.selectFsUserInformationCollectionVoById(id));
     }
+
     //医生确认
+    @Login
     @PostMapping("/doctorConfirm")
     public R doctorConfirm(@RequestBody FsUserInformationCollection collection){
         return fsUserInformationCollectionService.doctorConfirm(collection);
     }
 
+    @Login
     @GetMapping("/getCollectionList")
-    private R getCollectionList(UserInformationDoctorType2Param userInformationDoctorType2Param) {
+    public R getCollectionList(UserInformationDoctorType2Param userInformationDoctorType2Param) {
 
         PageHelper.startPage(userInformationDoctorType2Param.getPageNum(), userInformationDoctorType2Param.getPageSize());
         if (userInformationDoctorType2Param.getDoctorType()==2){
@@ -46,8 +51,8 @@ public class FsUserInformationCollectionController extends  AppBaseController {
 
     }
 
-
     //药师确认
+    @Login
     @PostMapping("/doctorType2Confirm")
     public R doctorType2Confirm(@RequestBody FsUserInformationCollection collection){
         return fsUserInformationCollectionService.doctorType2Confirm(collection);

+ 16 - 8
fs-doctor-app/src/main/java/com/fs/app/controller/InquiryOrderController.java

@@ -49,7 +49,7 @@ import org.springframework.web.bind.annotation.*;
 import javax.servlet.http.HttpServletRequest;
 import java.util.*;
 
-import static com.fs.his.utils.PhoneUtil.decryptPhone;
+import static com.fs.his.utils.PhoneUtil.maskForResponse;
 
 
 @Api("订单接口")
@@ -117,6 +117,7 @@ public class InquiryOrderController extends  AppBaseController {
     public R getInquiryOrderDetailsByOrderId(@RequestParam("orderId")Long orderId)
     {
         FsInquiryOrder order=inquiryOrderService.selectFsInquiryOrderByOrderId(orderId);
+        assertInquiryOrderAccess(order);
         return R.ok().put("data",order);
     }
 
@@ -133,10 +134,11 @@ public class InquiryOrderController extends  AppBaseController {
     {
         Map<String,Object> maps=new HashMap<>();
         FsInquiryOrder order=inquiryOrderService.selectFsInquiryOrderByOrderId(orderId);
+        assertInquiryOrderAccess(order);
         if (order.getPatientJson() != null&&!"".equals(order.getPatientJson())) {
             FsInquiryOrderPatientDTO fsInquiryOrderPatientDTO = JSON.parseObject(order.getPatientJson(), FsInquiryOrderPatientDTO.class);
             if(fsInquiryOrderPatientDTO.getMobile()!=null&&!"".equals(fsInquiryOrderPatientDTO.getMobile())){
-                fsInquiryOrderPatientDTO.setMobile(fsInquiryOrderPatientDTO.getMobile().replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2"));
+                fsInquiryOrderPatientDTO.setMobile(com.fs.his.utils.PhoneUtil.maskForResponse(fsInquiryOrderPatientDTO.getMobile()));
                 order.setPatientJson(JSON.toJSONString(fsInquiryOrderPatientDTO));
             }
 
@@ -225,9 +227,12 @@ public class InquiryOrderController extends  AppBaseController {
 
 
 
+    @Login
     @PutMapping("/updateRemark")
     public R edit(@RequestBody FsInquiryOrder fsInquiryOrder)
     {
+        FsInquiryOrder exist = inquiryOrderService.selectFsInquiryOrderByOrderId(fsInquiryOrder.getOrderId());
+        assertInquiryOrderAccess(exist);
         FsInquiryOrder o = new FsInquiryOrder();
         o.setOrderId(fsInquiryOrder.getOrderId());
         o.setDoctorRemark(fsInquiryOrder.getDoctorRemark());
@@ -249,6 +254,8 @@ public class InquiryOrderController extends  AppBaseController {
     @GetMapping("/getInquiryOrderReport")
     public R getInquiryOrderReport(@RequestParam("orderId")Long orderId)
     {
+        FsInquiryOrder order = inquiryOrderService.selectFsInquiryOrderByOrderId(orderId);
+        assertInquiryOrderAccess(order);
         FsInquiryOrderReport report=orderReportService.selectFsInquiryOrderReportByOrderId(orderId);
         return R.ok().put("data",report);
     }
@@ -353,26 +360,27 @@ public class InquiryOrderController extends  AppBaseController {
     }
 
 
+    @Login
     @GetMapping(value = "/queryPhone/{orderId}")
     @Log(title = "查看电话", businessType = BusinessType.GRANT)
     public R getPhone(@PathVariable("orderId") Long orderId)
     {
         FsInquiryOrder fsInquiryOrder = inquiryOrderService.selectFsInquiryOrderByOrderId(orderId);
+        assertInquiryOrderAccess(fsInquiryOrder);
         String patientJson = fsInquiryOrder.getPatientJson();
         if (patientJson != null&&!"".equals(patientJson)) {
             FsInquiryOrderPatientDTO fsInquiryOrderPatientDTO = JSON.parseObject(patientJson, FsInquiryOrderPatientDTO.class);
-            String phone = fsInquiryOrderPatientDTO.getMobile();
-            if (phone!=null&&phone.length()>11){
-               phone= decryptPhone(phone);
-            }
-
-            return R.ok().put("data",phone);
+            String phone = maskForResponse(fsInquiryOrderPatientDTO.getMobile());
+            return R.ok().put("data", phone == null ? "" : phone);
         }
         return R.ok().put("data","");
     }
 
+    @Login
     @PostMapping("/closeOrder")
     public R closeOrder(@RequestBody Long orderId){
+        FsInquiryOrder order = inquiryOrderService.selectFsInquiryOrderByOrderId(orderId);
+        assertInquiryOrderAccess(order);
         inquiryOrderService.closeOrder(orderId);
         logger.info("closeOrder: {}", orderId);
         return R.ok();

+ 34 - 11
fs-doctor-app/src/main/java/com/fs/app/controller/PatientController.java

@@ -1,25 +1,20 @@
 package com.fs.app.controller;
 
-import com.fs.common.annotation.Log;
-import com.fs.common.core.controller.BaseController;
-import com.fs.common.core.domain.AjaxResult;
+import com.fs.app.annotation.Login;
 import com.fs.common.core.domain.R;
-import com.fs.common.core.page.TableDataInfo;
-import com.fs.common.enums.BusinessType;
-import com.fs.common.utils.poi.ExcelUtil;
-import com.fs.his.domain.FsDoctor;
 import com.fs.his.domain.FsFollow;
+import com.fs.his.domain.FsInquiryOrder;
 import com.fs.his.domain.FsPatient;
 import com.fs.his.param.FsPatientListDParam;
+import com.fs.his.service.IFsFollowService;
+import com.fs.his.service.IFsInquiryOrderService;
 import com.fs.his.service.IFsPatientService;
-import com.fs.his.vo.FsFollowListDVO;
+import com.fs.his.utils.PhoneUtil;
 import com.fs.his.vo.FsPatientListDVO;
-import com.fs.his.vo.FsPatientVO;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
@@ -31,8 +26,12 @@ public class PatientController extends AppBaseController
 {
     @Autowired
     private IFsPatientService fsPatientService;
+    @Autowired
+    private IFsInquiryOrderService inquiryOrderService;
+    @Autowired
+    private IFsFollowService followService;
 
-
+    @Login
     @GetMapping("/getPatientList")
     public R getPatientList(FsPatientListDParam param)
     {
@@ -43,10 +42,34 @@ public class PatientController extends AppBaseController
         return R.ok().put("data",listPageInfo);
     }
 
+    @Login
     @ApiOperation("获取详情")
     @GetMapping("/getPatientByPatientId")
     public R getPatientByPatientId(@RequestParam("patientId")Long patientId, HttpServletRequest request){
         FsPatient patient=fsPatientService.selectFsPatientByPatientId(patientId);
+        if (patient == null) {
+            return R.error("患者不存在");
+        }
+        Long doctorId = Long.parseLong(getDoctorId());
+        // 问诊或随访任一有关联即可查看,避免误拦正常业务
+        FsInquiryOrder orderQuery = new FsInquiryOrder();
+        orderQuery.setDoctorId(doctorId);
+        orderQuery.setPatientId(patientId);
+        List<FsInquiryOrder> orders = inquiryOrderService.selectFsInquiryOrderList(orderQuery);
+        boolean owned = orders != null && !orders.isEmpty();
+        if (!owned) {
+            FsFollow followQuery = new FsFollow();
+            followQuery.setDoctorId(doctorId);
+            followQuery.setPatientId(patientId);
+            List<FsFollow> follows = followService.selectFsFollowList(followQuery);
+            owned = follows != null && !follows.isEmpty();
+        }
+        if (!owned) {
+            return R.error(403, "无权查看该患者");
+        }
+        if (patient.getMobile() != null) {
+            patient.setMobile(PhoneUtil.maskForResponse(patient.getMobile()));
+        }
         return R.ok().put("data",patient);
     }
 }

+ 13 - 0
fs-doctor-app/src/main/java/com/fs/app/controller/PrescribeController.java

@@ -105,6 +105,13 @@ public class PrescribeController extends  AppBaseController {
     public R getPrescribeById(@RequestParam("prescribeId")Long prescribeId)
     {
         FsPrescribe prescribe=prescribeService.selectFsPrescribeByPrescribeId(prescribeId);
+        if (prescribe == null) {
+            return R.error("处方不存在");
+        }
+        Long doctorId = Long.parseLong(getDoctorId());
+        if (prescribe.getDoctorId() != null && !prescribe.getDoctorId().equals(doctorId)) {
+            return R.error(403, "无权查看该处方");
+        }
         FsPrescribeDrug map=new FsPrescribeDrug();
         map.setPrescribeId(prescribeId);
         List<FsPrescribeDrug> drugs=prescribeDrugService.selectFsPrescribeDrugList(map);
@@ -133,6 +140,12 @@ public class PrescribeController extends  AppBaseController {
     public R getDoctorPrescribeById(@RequestParam("prescribeId")Long prescribeId)
     {
         FsDoctorPrescribe prescribe=doctorPrescribeService.selectFsDoctorPrescribeByPrescribeId(prescribeId);
+        if (prescribe == null) {
+            return R.error("处方不存在");
+        }
+        if (!prescribe.getDoctorId().equals(Long.parseLong(getDoctorId()))) {
+            return R.error(403, "无权查看该处方");
+        }
         FsDoctorPrescribeDrug map=new FsDoctorPrescribeDrug();
         map.setPrescribeId(prescribeId);
         List<FsDoctorPrescribeDrug> drugs=doctorPrescribeDrugService.selectFsDoctorPrescribeDrugList(map);

+ 7 - 0
fs-doctor-app/src/main/java/com/fs/app/controller/StoreOrderController.java

@@ -51,6 +51,13 @@ public class StoreOrderController extends  AppBaseController {
     @GetMapping("/getStoreOrderById")
     public R getStoreOrderById(@RequestParam("orderId") Long orderId, HttpServletRequest request){
         FsStoreOrder order=orderService.selectFsStoreOrderByOrderId(orderId);
+        if (order == null) {
+            return R.error("订单不存在");
+        }
+        Long doctorId = Long.parseLong(getDoctorId());
+        if (order.getDoctorId() != null && !order.getDoctorId().equals(doctorId)) {
+            return R.error(403, "无权查看该订单");
+        }
         List<FsStoreOrderItemListDVO> items=orderItemService.selectFsStoreOrderItemListDVOByOrderId(orderId);
         return R.ok().put("order",order).put("items",items);
     }

+ 82 - 0
fs-service/src/main/java/com/fs/framework/web/advice/PhoneMaskResponseBodyAdvice.java

@@ -0,0 +1,82 @@
+package com.fs.framework.web.advice;
+
+import com.fs.common.annotation.SkipPhoneMask;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.his.utils.PhoneMaskHelper;
+import org.springframework.core.MethodParameter;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * 全局出站手机号脱敏。
+ * 登录/注册等认证接口可通过 @SkipPhoneMask 或路径白名单豁免,避免破坏密文回传等既有业务。
+ */
+@Order(100)
+@ControllerAdvice
+public class PhoneMaskResponseBodyAdvice implements ResponseBodyAdvice<Object> {
+
+    @Override
+    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
+        if (returnType.getMethodAnnotation(SkipPhoneMask.class) != null) {
+            return false;
+        }
+        Class<?> containing = returnType.getContainingClass();
+        return containing == null || containing.getAnnotation(SkipPhoneMask.class) == null;
+    }
+
+    @Override
+    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
+                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
+                                  ServerHttpRequest request, ServerHttpResponse response) {
+        if (body == null) {
+            return null;
+        }
+        if (body instanceof byte[] || body instanceof CharSequence) {
+            return body;
+        }
+        if (isAuthExemptPath(request)) {
+            return body;
+        }
+        try {
+            if (body instanceof R || body instanceof AjaxResult || body instanceof TableDataInfo
+                    || body instanceof Map || body instanceof Iterable) {
+                return PhoneMaskHelper.maskObject(body);
+            }
+            return PhoneMaskHelper.maskObject(body);
+        } catch (Throwable t) {
+            return body;
+        }
+    }
+
+    /**
+     * 登录/注册/验证码等认证链路不脱敏,保证客户登录注册流程不受影响。
+     */
+    private boolean isAuthExemptPath(ServerHttpRequest request) {
+        if (request == null || request.getURI() == null || request.getURI().getPath() == null) {
+            return false;
+        }
+        String path = request.getURI().getPath().toLowerCase(Locale.ROOT);
+        return path.contains("/login")
+                || path.contains("/register")
+                || path.contains("/wxlogin")
+                || path.contains("/getphonecode")
+                || path.contains("/sendcode")
+                || path.contains("/sms")
+                || path.contains("/setphone")
+                || path.contains("/bindphone")
+                || path.contains("/editphone")
+                || path.contains("/forget")
+                || path.contains("/resetpwd")
+                || path.contains("/resetpassword");
+    }
+}

+ 171 - 0
fs-service/src/main/java/com/fs/his/utils/PhoneMaskHelper.java

@@ -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;
+    }
+}

+ 29 - 0
fs-service/src/main/java/com/fs/his/utils/PhoneUtil.java

@@ -123,6 +123,35 @@ public class PhoneUtil {
 
         return text;
     }
+
+    /**
+     * 出站统一脱敏:明文/密文/已脱敏均可,保证接口不返回完整手机号或原始密文。
+     */
+    public static String maskForResponse(String value) {
+        if (value == null || "".equals(value.trim())) {
+            return value;
+        }
+        String v = value.trim();
+        if (v.contains("****")) {
+            return v;
+        }
+        // 11 位明文
+        if (v.matches("^1\\d{10}$")) {
+            return ParseUtils.parsePhone(v);
+        }
+        // AES 密文或其他长串:尝试解密脱敏;失败则打码,禁止原文出站
+        if (v.length() > 11) {
+            String mk = decryptPhoneMk(v);
+            if (mk != null && mk.length() > 0) {
+                return mk;
+            }
+            if (v.length() <= 7) {
+                return "****";
+            }
+            return v.substring(0, 3) + "****" + v.substring(v.length() - 4);
+        }
+        return ParseUtils.parsePhone(v);
+    }
     /**
      * 用于查询 使用老的数据加密
      * @param text

+ 5 - 0
fs-service/src/main/resources/application-common.yml

@@ -14,6 +14,11 @@ fs:
   addressEnabled: false
   # 验证码类型 math 数组计算 char 字符验证
   captchaType: math
+  # SCRM 微信开放平台配置(生产请用环境变量覆盖并轮换密钥)
+  scrm:
+    wx:
+      app-id: ${SCRM_WX_APP_ID:wx703c4bd07bbd1695}
+      app-secret: ${SCRM_WX_APP_SECRET:034f5cc8d9b5151f9d25da9628541e35}
 #  jwt:
 #    # 加密秘钥
 #    secret: f4e2e52034348f86b67cde581c0f9eb5

+ 2 - 0
fs-user-app/src/main/java/com/fs/app/controller/AppLoginController.java

@@ -11,6 +11,7 @@ import com.fs.app.utils.WxUtil;
 import com.fs.common.VerifyCodeUtil;
 import com.fs.common.annotation.Log;
 import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.annotation.SkipPhoneMask;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.core.page.TableDataInfo;
@@ -76,6 +77,7 @@ import static com.fs.his.utils.PhoneUtil.encryptPhoneOldKey;
 @RestController
 @RequestMapping(value="/app/app")
 @Slf4j
+@SkipPhoneMask
 public class AppLoginController extends AppBaseController{
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
     @Autowired

+ 2 - 13
fs-user-app/src/main/java/com/fs/app/controller/CommonController.java

@@ -154,22 +154,11 @@ public class CommonController {
 
 	@Autowired
 	ICompanyMoneyLogsService logsService;
-	@ApiOperation("同步企业金额")
+    @ApiOperation("同步企业金额(已禁用:测试接口禁止外网暴露)")
 	@PostMapping("testUpdateCompanyMoney")
 	public R testUpdateCompanyMoney(@RequestBody TestMoneyParam param) throws Exception
 	{
-		List<CompanyMoneyLogs> logs=logsService.selectCompanyMoneyLogsByCompanyId(param.getCompanyId(),param.getLogsId().toString());
-		if(logs!=null){
-			BigDecimal companyMoney=param.getMoney();
-			for(CompanyMoneyLogs log:logs){
-				companyMoney=companyMoney.add(log.getMoney());
-				CompanyMoneyLogs logMap=new CompanyMoneyLogs();
-				logMap.setLogsId(log.getLogsId());
-				logMap.setBalance(companyMoney);
-				logsService.updateCompanyMoneyLogs(logMap);
-			}
-		}
-		return R.ok();
+		return R.error(403, "接口已禁用");
 	}
 
 

+ 35 - 4
fs-user-app/src/main/java/com/fs/app/controller/CompanyUserController.java

@@ -26,7 +26,8 @@ import com.fs.common.exception.ServiceException;
 import com.fs.common.exception.file.OssException;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.poi.ExcelUtil;
-import com.fs.common.utils.sign.Md5Utils;
+import com.fs.common.utils.security.SafeRemoteUrlUtils;
+import com.fs.common.utils.uuid.IdUtils;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.domain.CompanyUserCard;
 import com.fs.company.domain.CompanyUserUser;
@@ -139,8 +140,11 @@ public class CompanyUserController extends AppBaseController {
             if (!SecurityUtils.matchesPassword(param.getPassword(), companyUser.getPassword())) {
                 return R.error("密码不正确");
             }
-            redisCache.setCacheObject("company-user-token:" + Md5Utils.hash(companyUser.getUserId().toString()), companyUser.getUserId(), 100, TimeUnit.DAYS);
-            return R.ok().put("companyUserToken", Md5Utils.hash(companyUser.getUserId().toString())).put("user", companyUser);
+            // 随机 Token,仅写入不可推导键;不再双写 MD5(userId),避免会话被枚举劫持
+            String companyUserToken = IdUtils.fastSimpleUUID();
+            Long uid = companyUser.getUserId();
+            redisCache.setCacheObject("company-user-token:" + companyUserToken, uid, 100, TimeUnit.DAYS);
+            return R.ok().put("companyUserToken", companyUserToken).put("user", companyUser);
         } catch (Exception e) {
 
             return R.error("操作异常");
@@ -198,6 +202,7 @@ public class CompanyUserController extends AppBaseController {
         companyUser.setVoicePrintUrl(param.getVoicePrintUrl());
 
         //转换音频格式 mp3-wav
+        SafeRemoteUrlUtils.validateHttpUrl(param.getVoicePrintUrl());
         String s = AudioUtils.audioWAVFromUrl(param.getVoicePrintUrl());
         //保存文件并且上传存储桶
         System.out.println(s);
@@ -254,6 +259,11 @@ public class CompanyUserController extends AppBaseController {
     @Log(title = "小程序销售绑定医生", businessType = BusinessType.UPDATE)
     @PostMapping("/bindDoctorId")
     public R binDoctor(@RequestBody CompanyUser companyUser) {
+        // 已登录销售优先绑定自身,避免误绑;未带销售 Token 时保持原 body.userId 兼容旧调用
+        Long loginCompanyUserId = getCompanyUserIdOrNull();
+        if (loginCompanyUserId != null) {
+            companyUser.setUserId(loginCompanyUserId);
+        }
         return companyUserService.bindDoctor(companyUser);
     }
 
@@ -261,6 +271,10 @@ public class CompanyUserController extends AppBaseController {
     @Log(title = "小程序销售解除绑定医生", businessType = BusinessType.UPDATE)
     @GetMapping("/unBindDoctorId/{userId}")
     public R unBinDoctor(@PathVariable("userId") Long userId) {
+        Long loginCompanyUserId = getCompanyUserIdOrNull();
+        if (loginCompanyUserId != null) {
+            return companyUserService.unBindDoctor(loginCompanyUserId);
+        }
         return companyUserService.unBindDoctor(userId);
     }
 
@@ -482,8 +496,10 @@ public class CompanyUserController extends AppBaseController {
     /**
      * 获取用户信息采集详细信息
      */
+    @Login
     @GetMapping(value = "/informationCollection/{id}")
     public R getInformationCollectionInfo(@PathVariable("id") Long id) {
+        getCompanyUserId();
         return R.ok().put("data", fsUserInformationCollectionService.selectFsUserInformationCollectionVoById(id));
     }
 
@@ -528,12 +544,20 @@ public class CompanyUserController extends AppBaseController {
      * 删除用户信息采集
      */
     @DeleteMapping("/informationCollection/{ids}")
+    @Login
     public AjaxResult remove(@PathVariable Long[] ids) {
+        Long companyUserId = getCompanyUserId();
+        if (companyUserId == null) {
+            return AjaxResult.error("用户失效");
+        }
         return toAjax(fsUserInformationCollectionService.deleteFsUserInformationCollectionByIds(ids));
     }
 
     @GetMapping("/informationCollection/getInfo")
+    @Login
     public AjaxResult getInformationCollection(FsUserInformationCollection fsUserInformationCollection) {
+        Long companyUserId = getCompanyUserId();
+        fsUserInformationCollection.setCompanyUserId(companyUserId);
         return AjaxResult.success(fsUserInformationCollectionService.getInfo(fsUserInformationCollection));
     }
 
@@ -548,7 +572,13 @@ public class CompanyUserController extends AppBaseController {
     public AjaxResult uploadVoice(
             Long userId,
             String voicePrintUrl) throws Exception {
-        if (userId == null) userId = 123L;
+        if (userId == null) {
+            userId = getCompanyUserIdOrNull();
+        }
+        if (userId == null) {
+            return AjaxResult.error("用户失效");
+        }
+        SafeRemoteUrlUtils.validateHttpUrl(voicePrintUrl);
         VcCompanyUser vcCompanyUser = companyUserMapper.selectVcCompanyUserByCompanyUserId(userId);
         if (vcCompanyUser == null) {
             return AjaxResult.error("用户没有声纹槽位,请联系管理员");
@@ -720,6 +750,7 @@ public class CompanyUserController extends AppBaseController {
     }
 
     private File downloadFileFromUrl(String fileUrl) throws IOException {
+        SafeRemoteUrlUtils.validateHttpUrl(fileUrl);
         InputStream inputStream = null;
         FileOutputStream outputStream = null;
         try {

+ 10 - 2
fs-user-app/src/main/java/com/fs/app/controller/TalentController.java

@@ -169,7 +169,15 @@ public class TalentController extends  AppBaseController{
 
         //获取文件基本信息
         String originalFilename = file.getOriginalFilename();
-        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
+        if (originalFilename == null || originalFilename.trim().isEmpty()) {
+            return R.error("文件名非法");
+        }
+        // 仅取文件名,防止路径穿越
+        String safeBaseName = FilenameUtils.getName(originalFilename);
+        if (safeBaseName.contains("..") || safeBaseName.contains("/") || safeBaseName.contains("\\")) {
+            return R.error("文件名非法");
+        }
+        String suffix = safeBaseName.contains(".") ? safeBaseName.substring(safeBaseName.lastIndexOf(".")) : "";
         String fileType = file.getContentType();
 
         //如果是视频文件且需要缩略图
@@ -180,7 +188,7 @@ public class TalentController extends  AppBaseController{
             }
 
             //保存临时视频文件
-            String videoFileName = System.currentTimeMillis() + "_" + originalFilename;
+            String videoFileName = System.currentTimeMillis() + "_" + safeBaseName;
             File videoFile = new File(VIDEO_UPLOAD_DIR, videoFileName);
             file.transferTo(videoFile);
 

+ 1 - 0
fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserController.java

@@ -172,6 +172,7 @@ public class CourseFsUserController extends AppBaseController {
     @UserOperationLog(operationType = FsUserOperationEnum.ANSWER)
     @RepeatSubmit
     public R courseAnswer(@RequestBody FsCourseQuestionAnswerUParam param){
+        // 保持原逻辑:有 userId 用入参;否则从 Token 取(兼容 H5 既有调用)
         if (ObjectUtil.isEmpty(param.getUserId())){
             Long userId = Long.parseLong(getUserId());
             param.setUserId(userId);

+ 2 - 0
fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserLoginController.java

@@ -7,6 +7,7 @@ import cn.hutool.core.date.DateTime;
 import com.fs.app.annotation.UserOperationLog;
 import com.fs.app.controller.AppBaseController;
 import com.fs.common.core.domain.R;
+import com.fs.common.annotation.SkipPhoneMask;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.param.LoginMaWxParam;
 import com.fs.common.utils.IpUtil;
@@ -51,6 +52,7 @@ import static com.fs.his.utils.PhoneUtil.encryptPhone;
 @RestController
 @RequestMapping(value = "/app/wx/miniapp")
 @Slf4j
+@SkipPhoneMask
 public class CourseFsUserLoginController extends AppBaseController {
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
 

+ 6 - 1
fs-user-app/src/main/java/com/fs/app/controller/game/PlayerController.java

@@ -4,6 +4,7 @@ import cn.hutool.json.JSONUtil;
 import com.fs.app.annotation.Login;
 import com.fs.app.controller.AppBaseController;
 import com.fs.common.core.domain.R;
+import com.fs.common.utils.StringUtils;
 import com.fs.his.config.AppConfig;
 import com.fs.his.config.IntegralConfig;
 import com.fs.his.domain.FsUser;
@@ -39,7 +40,11 @@ public class PlayerController extends AppBaseController {
     @PostMapping("/updateCurrency")
     @ApiOperation("玩家货币更新")
     public R updateCurrency(@RequestBody FsUserAddIntegralParam param){
-        param.setUserId(param.getUserId());
+        // 保持原有游戏入参:客户端可传 userId;若已登录则优先用 Token,避免误用他人身份
+        String tokenUserId = getUserId();
+        if (StringUtils.isNotEmpty(tokenUserId)) {
+            param.setUserId(Long.parseLong(tokenUserId));
+        }
         param.setType(3);//游戏添加积分
         return userIntegralLogsService.addIntegral(param);
     }

+ 8 - 3
fs-user-app/src/main/java/com/fs/app/controller/store/AppLoginScrmController.java

@@ -9,6 +9,7 @@ import com.fs.app.param.FsUserLoginByWeChatParam;
 import com.fs.app.param.FsUserLoginParam;
 import com.fs.app.utils.WxUtil;
 import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.annotation.SkipPhoneMask;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.sign.Md5Utils;
@@ -21,6 +22,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -36,13 +38,16 @@ import java.util.concurrent.TimeUnit;
 @Api("app登录接口")
 @RestController
 @RequestMapping(value="/store/app/app")
+@SkipPhoneMask
 public class AppLoginScrmController extends AppBaseController {
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
     @Autowired
     private IFsUserScrmService userService;
 
-    private static final String APP_ID = "wx703c4bd07bbd1695";
-    private static final String APP_SECRET = "034f5cc8d9b5151f9d25da9628541e35";
+    @Value("${fs.scrm.wx.app-id:wx703c4bd07bbd1695}")
+    private String appId;
+    @Value("${fs.scrm.wx.app-secret:034f5cc8d9b5151f9d25da9628541e35}")
+    private String appSecret;
 
     @Autowired
     private RedisCache redisCache;
@@ -124,7 +129,7 @@ public class AppLoginScrmController extends AppBaseController {
                 return R.error("code不存在");
             }
             logger.info("zyp app微信登录,param:{}", param);
-            Map result = WxUtil.getAccessToken(param.getCode(),APP_ID,APP_SECRET);
+            Map result = WxUtil.getAccessToken(param.getCode(), appId, appSecret);
             String accessToken = result.get("access_token").toString();
             String unionid = result.get("unionid").toString();
             logger.info("zyp 获取unionid成功,unionid:{}", unionid);

+ 15 - 4
fs-user-app/src/main/java/com/fs/app/controller/store/CompanyUserScrmController.java

@@ -17,7 +17,8 @@ import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.CustomException;
 import com.fs.common.utils.CloudHostUtils;
 import com.fs.common.utils.ServletUtils;
-import com.fs.common.utils.sign.Md5Utils;
+import com.fs.common.utils.security.SafeRemoteUrlUtils;
+import com.fs.common.utils.uuid.IdUtils;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.domain.CompanyUserCard;
@@ -108,8 +109,10 @@ public class CompanyUserScrmController extends AppBaseController {
             if(!SecurityUtils.matchesPassword(param.getPassword(),companyUser.getPassword())){
                 return R.error("密码不正确");
             }
-            redisCache.setCacheObject("company-user-token:"+Md5Utils.hash(companyUser.getUserId().toString()),companyUser.getUserId(),5, TimeUnit.DAYS);
-            return R.ok().put("companyUserToken", Md5Utils.hash(companyUser.getUserId().toString()));
+            String companyUserToken = IdUtils.fastSimpleUUID();
+            Long uid = companyUser.getUserId();
+            redisCache.setCacheObject("company-user-token:"+companyUserToken, uid,5, TimeUnit.DAYS);
+            return R.ok().put("companyUserToken", companyUserToken);
         } catch (Exception e){
 
             return R.error("操作异常");
@@ -276,12 +279,20 @@ public class CompanyUserScrmController extends AppBaseController {
     @ApiOperation("上传声纹")
     @PostMapping("/addVoicePrintUrl")
     public R addVoicePrintUrl(@RequestBody companyUserAddPrintParam param) throws Exception {
-        Long userId=param.getCompanyUserId();
+        // 优先销售 Token;无 Token 时兼容原 body.companyUserId
+        Long userId = getCompanyUserIdOrNull();
+        if (userId == null) {
+            userId = param.getCompanyUserId();
+        }
+        if (userId == null) {
+            return R.error(403, "用户失效");
+        }
         CompanyUser companyUser = new CompanyUser();
         companyUser.setUserId(userId);
         companyUser.setVoicePrintUrl(param.getVoicePrintUrl());
 
         //转换音频格式 mp3-wav
+        SafeRemoteUrlUtils.validateHttpUrl(param.getVoicePrintUrl());
         String s = AudioUtils.audioWAVFromUrl(param.getVoicePrintUrl());
 
         //保存文件并且上传存储桶

+ 2 - 5
fs-user-app/src/main/java/com/fs/app/controller/store/CourseScrmController.java

@@ -546,15 +546,12 @@ public class CourseScrmController extends AppBaseController {
 
 
     /**
-     * 手动根据销售员工信息发放奖励
-     * @param userId
-     * @return
+     * 手动根据销售员工信息发放奖励(已禁用:测试接口禁止外网暴露)
      */
     @GetMapping("/sendRewardByTest")
     public R sendRewardByTest(@RequestBody Long userId)
     {
-        logger.info("zyp \n【发放奖励】6:{}",userId);
-        return  storePaymentService.sendRewardByTest(userId);
+        return R.error(403, "接口已禁用");
     }
 
 }

+ 0 - 7
fs-user-app/src/main/java/com/fs/app/interceptor/AuthorizationInterceptor.java

@@ -30,7 +30,6 @@ public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
     @Autowired
     FsUserMapper fsUserMapper;
     public static final String USER_KEY = "userId";
-    private static final String SKIP_AUTH_HEADER = "X-Skip-Auth";
 
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
@@ -45,12 +44,6 @@ public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
             return true;
         }
 
-        // 请求头存在跳过认证标识,直接放行
-        String skipAuth = request.getHeader(SKIP_AUTH_HEADER);
-        if (StringUtils.isNotEmpty(skipAuth)) {
-            return true;
-        }
-
         //获取用户凭证
         String token = request.getHeader(jwtUtils.getHeader());
         if(StringUtils.isBlank(token)){

+ 5 - 17
fs-user-app/src/main/java/com/fs/framework/aspectj/UserOperationLogAspect.java

@@ -51,8 +51,6 @@ public class UserOperationLogAspect {
 
     private final ObjectMapper objectMapper = new ObjectMapper();
     private static final ThreadLocal<FsUserOperationLog> LOG_HOLDER = new ThreadLocal<>();
-    private static final String SKIP_AUTH_HEADER = "X-Skip-Auth";
-
     @Pointcut("@annotation(com.fs.app.annotation.UserOperationLog)")
     public void logPointcut() {}
 
@@ -115,22 +113,12 @@ public class UserOperationLogAspect {
             if (annotation == null) return;
             operationLog.setOperationType(annotation.operationType().getLabel());
 
-            //用户
+            //用户:仅从已校验的 APPToken 解析,禁止请求头伪造身份
             Long userId = null;
-            // 优先从 X-Skip-Auth 请求头取 userId(跳过token校验场景)
-            String skipAuth = ServletUtils.getRequest().getHeader(SKIP_AUTH_HEADER);
-            if (StringUtils.isNotEmpty(skipAuth)) {
-                try {
-                    userId = Long.valueOf(skipAuth);
-                } catch (NumberFormatException ne) {
-                    log.info("X-Skip-Auth 请求头不是有效的userId: {}", skipAuth);
-                }
-            } else {
-                try {
-                    userId = Long.valueOf(jwtUtils.getClaimByToken(ServletUtils.getRequest().getHeader("APPToken")).getSubject().toString());
-                } catch (Exception ie) {
-                    log.info("获取用户id失败");
-                }
+            try {
+                userId = Long.valueOf(jwtUtils.getClaimByToken(ServletUtils.getRequest().getHeader("APPToken")).getSubject().toString());
+            } catch (Exception ie) {
+                log.info("获取用户id失败");
             }
             if (userId == null) {
                 LOG_HOLDER.set(operationLog);