主体传播并发问题:Java应用层面能否修复?
问题背景
基于SAP Cloud SDK 5.1.0在BTP开发的应用,通过Cloud Connector实现主体连接本地S/4HANA OData V2服务时,出现严重权限问题:有权限的用户A正常访问数据后,无权限的用户B、C居然也能查看同一份S/4数据。排查确认三个用户的请求共用了同一个SAP_SESSIONID会话Cookie,SAP官方指出这是会话未终止、被复用导致的问题。
原因定位
这个问题确实源于Java应用的会话缓存逻辑。SAP Cloud SDK默认会对Destination实例进行缓存,其中包含了S/4HANA返回的会话Cookie(如SAP_SESSIONID)。当多个用户请求复用同一个缓存的Destination时,后续用户会直接使用前一个用户的会话Cookie,导致S/4HANA误判为同一用户操作,绕过了权限校验。
应用层面修复方案
完全可以在Java应用层面修复,核心思路是确保每个用户的请求使用独立的会话,避免Destination缓存复用会话Cookie,具体有几种可行方案:
方案1:禁用Destination的Cookie存储
修改获取Destination的代码,显式禁用Cookie持久化缓存,让每次请求都重新建立会话:
// 关键修改:添加disableCookieStorage()禁用Cookie缓存 HttpDestination DEST_PHYSICAL_STOCK = DestinationAccessor.getDestination(destinationName) .asHttp() .decorate(HttpDestination::disableCookieStorage);
该配置会让SDK在每次请求时不保存和复用Cookie,每个用户的请求都会使用自身身份凭证重新发起会话,彻底避免会话共享。
方案2:配置Destination全局禁用Cookie缓存
如果不想修改代码,也可以在BTP的Destination配置界面添加属性:
- 键:
CookieStorageDisabled - 值:
true
SDK会自动读取该配置,禁用所有基于此Destination的请求的Cookie缓存。
方案3:显式终止会话(可选增强)
如果需要主动回收会话资源,可以在请求结束后调用S/4HANA的会话注销接口(通常是/sap/opu/odata/sap/.../Logout这类OData服务),确保会话被主动销毁,避免Cookie被意外复用。
代码调整示例
针对你提供的代码,只需要修改Destination获取部分即可修复问题,调整后的完整代码片段:
// 关键修改:添加disableCookieStorage()禁用Cookie缓存 HttpDestination DEST_PHYSICAL_STOCK = DestinationAccessor.getDestination(destinationName) .asHttp() .decorate(HttpDestination::disableCookieStorage); List<PhysicalStock> materialList = null; PhysicalStockFluentHelper odataService = new DefaultEWMSTOCKDATASERVICEService() .withServicePath("/sap/opu/odata/RB4R/EWM_STOCK_DATA_SRV") .getAllPhysicalStock(); // 以下过滤逻辑保持不变 ExpressionFluentHelper<PhysicalStock> materialFilter = PhysicalStock.MATERIAL.eq(material); ExpressionFluentHelper<PhysicalStock> plantFilter = PhysicalStock.PLANT.ne(" "); SystemToPlantFilterMap sys2PlantMap = null; for (int j = 0; j < filter.size(); j++) { if (systemName.equals(filter.get(j).getSystem())) { sys2PlantMap = filter.get(j); } } List<String> uniquePlantList = extractPlantData(sys2PlantMap); if ((applyPlantFilter && uniquePlantList != null || applyBatchFilterEWM || applySLocationFilterEWM) && uniquePlantList != null && !uniquePlantList.isEmpty() && uniquePlantList.size() > 0) { plantFilter = getEWMPlantFilter(sys2PlantMap, uniquePlantList, applyBatchFilterEWM, applySLocationFilterEWM); } ExpressionFluentHelper<PhysicalStock> allFilter = materialFilter; allFilter = allFilter.and(plantFilter); if (applyWareHouseFilter) { wareHouseFilter = getWarehouseFilter(globalViewFilterDTO.getWareHouse()); allFilter = allFilter.and(wareHouseFilter); } // 其他过滤逻辑保持不变... odataService = odataService.filter(allFilter); log.error("Search Started +-+-+-" + username + "," + material); materialList = odataService.executeRequest(DEST_PHYSICAL_STOCK); log.error("Search Ended +-+-+-" + username + "," + material +"System Name = " + systemName + " materialList.size = " + materialList.size());
验证要点
修复后需要验证:
- 不同用户请求的
SAP_SESSIONID不再相同 - 无权限用户无法访问超出自身权限的数据
- 有权限用户的正常访问不受影响
内容的提问来源于stack exchange,提问作者Jerry Zhang

