Просмотр исходного кода

fix(s6): 收口 IPQC 跨租户读取 P0 (server 1.0.298 / Web 2.4.274)

IpqcInspectionService list/detail 不再信任前端 tenantId、移除 [AllowAnonymous],
租户只来自认证后 JWT(严格:超管未选/无 Token 拒绝,无默认回退、不读 query tenantId)。

- IpqcInspectionService:去 AllowAnonymous + 注入 UserManager + ResolveTenantOrThrow;
  GetList/GetDetail 改用可信租户;GetDetail 去掉 tenantId query 参数
- IpqcInspectionDto:移除 IpqcInspectionListInput.TenantId
- 前端 ipqcInspection.ts:list/detail 去 withAidopTenantParams(不动公共实现)

运行时实测:无Token→401、超管未选→400、A 传 tenantId=B/系统→按 A 隔离(读不到)、
detail 头+明细同租户隔离。不碰 S1-S4 / NumberRuleService / AidopTenantHelper。
YY968XX 5 дней назад
Родитель
Сommit
bf1c246cea

+ 1 - 1
Web/package.json

@@ -1,7 +1,7 @@
 {
   "name": "admin.net",
   "type": "module",
-  "version": "2.4.273",
+  "version": "2.4.274",
   "packageManager": "pnpm@10.32.1",
   "lastBuildTime": "2026.03.15",
   "description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",

+ 2 - 3
Web/src/views/aidop/s6/api/ipqcInspection.ts

@@ -1,5 +1,4 @@
 import service from '/@/utils/request';
-import { withAidopTenantParams } from '../../api/aidopTenant';
 
 export interface Paged<T> {
 	total: number;
@@ -55,7 +54,7 @@ export interface IpqcInspectionRow {
 
 export function fetchIpqcInspectionList(params: any) {
 	return service
-		.get<Paged<IpqcInspectionRow>>('/api/IpqcInspection/list', { params: withAidopTenantParams(params) })
+		.get<Paged<IpqcInspectionRow>>('/api/IpqcInspection/list', { params })
 		.then((r) => r.data);
 }
 
@@ -133,6 +132,6 @@ export interface IpqcInspectionDetail {
 
 export function fetchIpqcInspectionDetail(id: number) {
 	return service
-		.get<IpqcInspectionDetail>('/api/IpqcInspection/detail', { params: withAidopTenantParams({ id }) })
+		.get<IpqcInspectionDetail>('/api/IpqcInspection/detail', { params: { id } })
 		.then((r) => r.data);
 }

+ 3 - 3
server/Admin.NET.Web.Entry/Admin.NET.Web.Entry.csproj

@@ -11,9 +11,9 @@
     <GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
     <Copyright>Admin.NET</Copyright>
     <Description>Admin.NET ͨ��Ȩ�޿���ƽ̨</Description>
-    <AssemblyVersion>1.0.297</AssemblyVersion>
-    <FileVersion>1.0.297</FileVersion>
-    <Version>1.0.297</Version>
+    <AssemblyVersion>1.0.298</AssemblyVersion>
+    <FileVersion>1.0.298</FileVersion>
+    <Version>1.0.298</Version>
   </PropertyGroup>
 
   <ItemGroup>

+ 0 - 3
server/Plugins/Admin.NET.Plugin.AiDOP/Manufacturing/Dto/IpqcInspectionDto.cs

@@ -23,9 +23,6 @@ public class IpqcInspectionListInput
 
     /// <summary>排序方向(asc / desc)</summary>
     public string? SortOrder { get; set; }
-
-    /// <summary>租户 ID(前端 withAidopTenantParams 注入;为空则不按租户过滤)</summary>
-    public long? TenantId { get; set; }
 }
 
 /// <summary>

+ 29 - 8
server/Plugins/Admin.NET.Plugin.AiDOP/Manufacturing/IpqcInspectionService.cs

@@ -14,15 +14,36 @@ namespace Admin.NET.Plugin.AiDOP.Manufacturing;
 /// </summary>
 [ApiDescriptionSettings(Order = 306, Description = "过程检验单")]
 [Route("api/IpqcInspection")]
-[AllowAnonymous]
 [NonUnify]
 public class IpqcInspectionService : IDynamicApiController, ITransient
 {
+    /// <summary>主/系统租户哨兵:超管未选择目标租户时其 JWT TenantId 即此值,拒绝作为业务租户。</summary>
+    private const long MainTenantId = 1300000000001L;
+
     private readonly ISqlSugarClient _db;
+    private readonly UserManager _userManager;
 
-    public IpqcInspectionService(ISqlSugarClient db)
+    public IpqcInspectionService(ISqlSugarClient db, UserManager userManager)
     {
         _db = db;
+        _userManager = userManager;
+    }
+
+    /// <summary>
+    /// IPQC 只读查询的可信租户解析(严格语义,无任何默认回退、不读前端 tenantId):
+    ///  - 普通租户用户:返回认证后的 JWT TenantId(&gt;0);
+    ///  - 无 Token(TenantId&lt;=0):拒绝(正常已被认证层 401 拦截,此处为纵深防御);
+    ///  - 超管未选择目标租户(TenantId==主租户):拒绝,必须先选择目标租户。
+    /// 前端传入的 tenantId 一律忽略,绝不作为切换租户的权威来源。
+    /// </summary>
+    private long ResolveTenantOrThrow()
+    {
+        var tid = _userManager.TenantId;
+        if (tid <= 0)
+            throw Oops.Oh("无法确定当前租户,请重新登录或选择目标租户");
+        if (_userManager.SuperAdmin && tid == MainTenantId)
+            throw Oops.Oh("超级管理员操作前必须选择目标租户");
+        return tid;
     }
 
     /// <summary>
@@ -39,9 +60,9 @@ public class IpqcInspectionService : IDynamicApiController, ITransient
         var where = new List<string> { "1=1" };
         var pars = new List<SugarParameter>();
 
-        // 强制租户过滤:qms_gcjyd/qms_gcjydzb 全库共享无 tenant,标准层按源 tenant_id 落库
-        // 这里必须强制按当前租户过滤(显式参数 > JWT > query > 默认租户),杜绝跨租户可见。
-        var tid = AidopTenantHelper.Resolve(App.HttpContext, input.TenantId);
+        // 强制租户过滤:qms_gcjyd/qms_gcjydzb 全库共享无 tenant,标准层按源 tenant_id 落库
+        // 租户只来自认证后的可信上下文(严格 JWT),忽略前端传入的 input.TenantId,杜绝跨租户可见。
+        var tid = ResolveTenantOrThrow();
         where.Add("m.tenant_id = @TenantId");
         pars.Add(new SugarParameter("@TenantId", tid));
         if (!string.IsNullOrWhiteSpace(input.BillNo))
@@ -99,12 +120,12 @@ public class IpqcInspectionService : IDynamicApiController, ITransient
     /// </summary>
     [DisplayName("过程检验单详情")]
     [HttpGet("detail")]
-    public async Task<IpqcInspectionDetailDto?> GetDetail([FromQuery] long id, [FromQuery] long? tenantId)
+    public async Task<IpqcInspectionDetailDto?> GetDetail([FromQuery] long id)
     {
         if (id <= 0) return null;
 
-        // 强制租户过滤(同 GetList):显式参数 > JWT > query > 默认租户,杜绝跨租户读取详情。
-        var tid = AidopTenantHelper.Resolve(App.HttpContext, tenantId);
+        // 强制租户过滤(同 GetList):租户只来自认证后的可信上下文,忽略前端 tenantId,杜绝跨租户读取详情。
+        var tid = ResolveTenantOrThrow();
         var headPars = new List<SugarParameter> { new("@Id", id), new("@TenantId", tid) };
         var headWhere = "m.id = @Id AND m.tenant_id = @TenantId";