【问题标题】:How do I handle the need for CSFR token when using SAP Cloud SDK?使用 SAP Cloud SDK 时如何处理对 CSRF 令牌的需求?
【发布时间】:2020-04-29 22:25:50
【问题描述】:

我正在使用 SAP Cloud SDK for Java 在 S/4 中对 SalesOrder API 执行 CRUD。一切都很好,因为我可以从 Postman 执行这些操作。但是,只有在我包含预请求脚本以获取 this blog post 中所述的 csrf 令牌时,来自 Postman 的这些请求才有效

如果我在没有博客文章中概述的预请求脚本的情况下运行请求,我会收到“403 Forbidden”。正如我所说,它适用于 Postman,但我想了解如何在不需要此脚本的情况下处理它,例如,如果我正在从另一个应用程序发出请求。 SDK是否允许我以某种方式从应用程序代码处理这个问题。也许我错过了什么。

感谢您的宝贵时间。

编辑: 我不是直接从 Postman 向 S/4 提出请求。我部署了一个应用程序,它使用 Cloud SDK 向 S/4 发出请求。如果我在发送之前使用预请求脚本获取 CSFR 令牌并将其附加到请求中,则它可以工作,但如果我不使用,则为 403。所以,如果我们想象我不是在使用 Postman,而是在某个地方使用一些 ui 来填写表格并发送这个请求,我的理解是,我不应该像你建议的那样担心这个令牌,我的中间服务使用SDK 和 VDM 应该为我处理这个问题。这是我很难理解的。

这是servlet代码:

@Override
protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
        throws ServletException, IOException {

    String body = IOUtils.toString(request.getReader());
    JSONObject so = new JSONObject(body);
    String distributionChannel = so.get("DistributionChannel").toString();
    String salesOrderType = so.get("SalesOrderType").toString();
    String salesOrganization = so.get("SalesOrganization").toString();
    String soldToParty = so.get("SoldToParty").toString();
    String organizationDivision = so.get("OrganizationDivision").toString();
    String material = so.get("Material").toString();
    String requestedQuantityUnit = so.get("RequestedQuantityUnit").toString();

    SalesOrderItem salesOrderItem = SalesOrderItem.builder()
    .material(material)
    .requestedQuantityUnit(requestedQuantityUnit).build();

    SalesOrder salesOrder = SalesOrder.builder()
    .salesOrderType(salesOrderType)
    .distributionChannel(distributionChannel)
    .salesOrganization(salesOrganization)
    .soldToParty(soldToParty)
    .organizationDivision(organizationDivision)
    .item(salesOrderItem)
    .build();

    try {
        final ErpHttpDestination destination = DestinationAccessor.getDestination(DESTINATION_NAME).asHttp()
                .decorate(DefaultErpHttpDestination::new);
        final SalesOrder storedSalesOrder = new CreateSalesOrderCommand(destination, new DefaultSalesOrderService(),
                salesOrder).execute();
        response.setStatus(HttpServletResponse.SC_CREATED);
        response.setContentType("application/json");
        response.getWriter().write(new Gson().toJson(storedSalesOrder));
        logger.info("Succeeded to CREATE {} sales order", storedSalesOrder);

    } catch (final Exception e) {
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        logger.error(e.getMessage(), e);
        logger.error("Failed to CREATE sales order", e);
    }
}

还有 CreateSalesOrder 命令:

public SalesOrder execute() {
    return ResilienceDecorator.executeSupplier(this::run, myResilienceConfig);
}

protected SalesOrder run() {
    try {
        return salesOrderService.createSalesOrder(salesOrder).execute(destination);
    } catch (final ODataException e) {
        throw new ResilienceRuntimeException(e);
    }
}

我使用的是 3.16.1 版本的 SDK,并已在清单中将 SDK 的日志记录级别设置为 DEBUG:

SET_LOGGING_LEVEL: '{ROOT: INFO, com.sap.cloud.sdk: DEBUG}'

在 logback 中将日志级别记录为 DEBUG

如果我从请求中删除预请求脚本并发送它,我会收到 403 响应,并且日志会显示以下消息:

"logger":"com.sap.cloud.sdk.service.prov.api.security.AuthorizationListener","thread":"http-nio-0.0.0.0-8080-exec-4","level" :"DEBUG","categories":[],"msg":"正在阅读 用户主体”

"logger":"com.sap.cloud.sdk.service.prov.api.security.AuthorizationListener","thread":"http-nio-0.0.0.0-8080-exec-4","level" :"DEBUG","categories":[],"msg":"在请求结束时销毁授权。" }

"logger":"com.sap.cloud.sdk.service.prov.api.security.AuthorizationService","thread":"http-nio-0.0.0.0-8080-exec-4","level" :"DEBUG","categories":[],"msg":"销毁授权 JWT 令牌。" }

【问题讨论】:

    标签: sap-cloud-platform sap-cloud-sdk s4hana


    【解决方案1】:

    由于其他答案侧重于应用程序与 S/4 通信,并且您调整了问题以明确表示您是指用户(例如邮递员)与应用程序通信,因此我将提供一些额外信息。

    正如其他答案所述,对 S/4 系统(或任何 OData 端点)的 CSRF 处理是在 OData VDM 一侧自动处理的。

    您现在遇到的是 SAP Cloud SDK Maven Archetypes 的安全默认配置,默认情况下会激活 RestCsrfPreventionFilter。 这个过滤器自动保护所有非GET 端点免受 CSRF 的影响,要求您在您提供的请求之前获取 CSRF 令牌。 这与后台对 S/4 系统的 OData VDM 调用完全无关。

    为了解决您的问题,现在有以下三个步骤:

    • 使用GET 端点而不是POST
      • 可能仅作为临时解决方法
    • 暂时从您的web.xml 中删除RestCsrfPreventionFilter
      • 这应该用于生产用途,但可能会使您在初始评估中的生活更轻松。
    • “接受它”
      • 由于这是保护您的应用程序免受 CSRF 攻击的常用模式,因此建议保留过滤器并根据需要执行 CSRF-Token“流”。

    进一步阅读

    【讨论】:

      【解决方案2】:

      墨者

      你是对的,使用Postman 之类的API 工具,您必须先发出HEAD 请求才能获得CSRF 令牌。

      但是,在Cloud SDK for Java 中,当您发出任何CRUD 请求 时,我们会为您获取和刷新CSRF 令牌。

      这是一个读取 Saler Oder 商品并在之后更新它的示例:

      // Create a new sales order item
      SalesOrderItem item = new SalesOrderItem();
      item.setSalesOrder(SALES_ORDER);
      item.setNetAmount(new BigDecimal(NET_VALUE));
      item = service.createSalesOrderItem(item).execute(destination).getResponseEntity().get();
      
      // Modify it with a PATCH update to 9000 net value
      item.setNetAmount(new BigDecimal(NET_VALUE_UPDATED));
      ModificationResponse<SalesOrderItem> response = service.updateSalesOrderItem(item).modifyingEntity().execute(destination);
      

      试试看,如果它适合你,请告诉我。如果您遇到任何困难,我们很乐意为您提供帮助。

      【讨论】:

      • 啊,好吧。所以,如果我有一个真正的客户端应用程序,SDK 会立即为我处理这个问题,我不必担心添加标题或类似的东西,对吧?很高兴知道。
      • 没错,Inkers,在您从销售订单服务定义生成客户端后,所有必需的标头(如 CSRFEtag)都将为您处理。我添加了一个关于如何创建和随后更新实体的小代码片段示例。
      • 当然,如果Entity 在您的请求或类似请求之间被其他人更新,您必须注意服务器返回的任何错误,以防不匹配的Etag情况。我还建议您查看我们的教程,了解更多详细信息:developers.sap.com/tutorials/…
      • 嗨,Artyom。我不确定我是否很好地解释了我原来的问题。请更详细地查看我的编辑。啤酒花这有助于进一步澄清。
      【解决方案3】:

      SDK 尝试在execute(destination) 内自动获取 CSRF 令牌。这发生在发出实际请求之前。如果尝试成功,则令牌将包含在请求中。如果没有,则无论如何都会发送请求。

      如果您认为这不正确,请提高日志级别以调试所有 com.sap.cloud.sdk 软件包。此外,很高兴看到进出的实际 HTTP 请求,您可以通过将org.apache.http.wire 的日志级别也设置为调试来启用这些请求。然后在此处附加堆栈跟踪以及您正在使用的 SDK 版本以及您正在调用的确切代码。

      【讨论】:

        猜你喜欢
        • 2018-07-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-19
        • 2019-02-26
        • 2017-04-16
        • 1970-01-01
        • 2013-05-05
        相关资源
        最近更新 更多