添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

身份验证与授权


多步操作公用 API 应在业务用户的语境下进行。API 用户需要正确配置身份验证和授权,以在 SAC 中指定所需的业务用户。通常有两种主要选项来配置身份验证和授权。

OAuth2 授权码工作流


详见 https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/14cac91febef464dbb1efce20e3f1613/0c1fb5e6ef1f46acb8377... ,“Configuring OAuth 2.0 Authorization Code Grant Workflow (3-legged OAuth) ”这部分介绍了如何在 SAC 中按照 OAuth2 授权码流对用户进行身份验证和授权。(由于多步操作 API 应在业务用户语境下进行,因此选择“交互式使用”作为 OAuth 客户端的“目的”)。

OAuth 2.0 SAML Bearer Assertion 工作流


除了授权码解决方案外,你还可以利用 OAuth2 SAML Bearer Assertion 工作流来进行身份验证和授权,前提是用户已经在 SAC 中添加了可信的 IdP。应用授权码工作流的一个限制是通常需要终端用户输入凭据登录 SAC,从而获取授权码并继续认证过程。考虑到大部分情况下,对公用 API 的调用是嵌在完全自动化的流程中的,并且不能让用户与其交互。我们建议你在这种情况下将 OAuth2 SAML Bearer Assertion 工作流视为备选解决方案。
你可以使用由你的 IdP 颁发的 SAML Bearer Token 来获取访问令牌。这篇文章 https://blogs.sap.com/2021/04/09/sap-analytics-cloud-app-integration-with-oauth2samlbearerassertion-... 介绍了在 SAC 应用集成中可以应用的 OAuth2 SAML Bearer Assertion 流,并提供了一些详细的示例,介绍了如何在有或没有使用 SAP BTP Destination 服务的情况下应用这个流。

获取 CSRF 令牌


在使用 POST 方法发送 HTTP 请求之前(在多操作触发 API 中使用),你需要先获取一个有效的 CSRF 令牌。
你可以通过向 {tenant-url}/api/v1/csrf 发送 GET 请求,在请求中包括头部 x-csrf-token: fetch 来获取令牌。然后, CSRF 令牌会在响应头 x-csrf-token 中返回。
一旦你获得了令牌,你就可以在同一会话中使用它来发送 POST 请求(将 Cookie 中的 JSESSIONID 与 CSRF 请求保持一致)。请求中必须包含 x-csrf-token:{token} 头部。

API 的定义


请参考 SAC 帮助文档: https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/14cac91febef464dbb1efce20e3f1613/e5ade1ed7c274d929a18b... ,获取 API 的定义的细节,这一内容不会在这篇文章中重复说明。

使用一些常见的HTTP客户端来尝试API


在下面的示例中,我们将使用 Postman 来尝试 API,因为它提供了友好的用户界面和简单的配置,以便促进 OAuth2 授权码流。你也可以使用其他典型的 HTTP 客户端来尝试 API(例如 curl: https://curl.se/docs/manual.html ,swagger-ui: https://swagger.io/tools/swagger-ui/ 等),只要它们已经内置了相关的身份验证/授权流,或者你也可以自定义解决方案,按照授权码流或 SAML Bearer Assertion 流获取访问令牌,并使用授权报头发送令牌。
(要在 swagger-ui 中使用 API,您可以从 https://api.sap.com/api/MultiActions_API/overview 下载API规范作为参考)

OAuth2 授权码工作流


  • 在 SAC 中创建一个 OAuth 客户端,将用途设置为“交互使用”,将重定向 URI 设置为: https://www.getpostman.com/oauth2/callback, 这是 postman 需要的。

  • 转到 Postman 中的请求集合的“Authorization”选项卡,将“configure new token”部分定义为:

  • 配置完成后,点击 postman 中“Authorization”选项卡底部的“ configure new token ”。在弹出窗口中登录SAC 后,访问令牌会被获取。

  • 接下来,访问令牌就可以用于访问多步操作公用 API 了。同时,使用 POST 方法访问 API 需要 csrf 令牌。先使用以下请求获取 csrf 令牌。

  • 你将会在响应报头中拿到 csrf 令牌:
    5. 在下面的 POST 请求中使用 csrf 令牌来执行多步操作。csrf 令牌仅在同一会话中有效,因此请确保以下请求使用相同的 cookie (这通常由 Postman 默认处理,一般情况下你不需要在以下请求中显式设置 cookie)
    6. 使用查询状态 API 来获取多步操作执行的状态。

    OAuth 2.0 SAML Bearer Assertion 工作流


    与上述授权码流相比,使用 OAuth 2.0 SAML Bearer Assertion 尝试 API 的唯一区别在于获取访问令牌的方式,而在其他部分,它们应该是相同的。
    https://blogs.sap.com/2021/04/09/sap-analytics-cloud-app-integration-with-oauth2samlbearerassertion-... 提供了使用和不使用 BTP destination 服务获取访问令牌的示例。例如,你可以参考 https://blogs.sap.com/2021/04/08/how-to-generate-saml-bearer-assertion-token-for-oauth2samlbearerass... 来创建一个nodejs程序,以便在 OAuth 2.0 SAML Bearer Assertion 流之后获得访问令牌(如果手头没有 BTP destination)。如果你能够在你的解决方案中使用 BTP destination,那将会变得更加容易 https://blogs.sap.com/2021/03/24/oauth2samlbearerassertion-flow-with-the-sap-btp-destination-service... 说明了如何利用 BTP destination 来使用 OAuth 2.0 SAML Bearer Assertion 流访问 SAC。
    然后从前面授权码流示例中的步骤4开始,使用访问令牌访问 API,下面的步骤应该是相同的。

    从 ABAP/NetWeaver 栈调用 API


    可以从 ABAP/NetWeaver 栈中使用多操作公用 API,因此可以将其与BW, BPC等产品集成,只要它们提供定制的 ABAP 出口。在下面的示例中,我们将利用 BTP destination 服务(作为身份提供者,它可以被作为 IdP 角色的其他服务取代) 来实现 OAuth2 SAML Beaerer Assertion 流,从而可以从 ABAP 程序调用多步操作公用 API,无需用户干预,并且可以嵌入在 ABAP/NetWeaver 侧的自动化作业中。
    还请注意,下面所示的过程非常常见,您也可以通过利用所选技术提供的相应 http 客户端,将ABAP 栈替换为其他编程环境。
    工作流的实现步骤如下:

    在 SAC 中创建 OAuth Client


    在 SAC 中创建一个 OAuth client,用途设置为“交互式使用”,并且不需要定义重定向url。

    在 BTP 中 创建 destination 以及 destination 服务


    这篇文章 https://blogs.sap.com/2021/03/24/oauth2samlbearerassertion-flow-with-the-sap-btp-destination-service... 全面解释了如何利用 BTP destination 服务作为 IdP 在 SAC 中实现 OAuth 2.0 SAML Bearer 身份认证( https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/consuming-destination-service )。在当前场景的语境中,以下是在 BTP 中设置 destination 和 destination 服务的一些关键步骤:
    在 BTP 子帐户中创建 destination
    在BTP子帐户下创建一个目标,其身份验证类型为:OAuth2 SAML Beaerer Assertion。请参考下面的快照进行字段映射:
    下载 IDP metadata,记下在 SAC 中添加信任 IDP 时将使用的 entityID。
    Download Trust 并复制这里的 public certificate value ,这也将用于在 SAC 中添加信任 IdP:
    在 BTP 中创建 destination 服务实例(如果服务之前不存在)
    如果之前没有创建可用的服务实例,则在 BTP 中创建 destination 服务实例
    打开服务密钥,记下 client id 和 client secrete,它们将用于使用 OAuth2 client 凭证方法访问 destination 服务实例。
    REPORT z_run_multi_actions_in_sac.
    PARAMETERS:
    pv_ma_id TYPE string LOWER CASE DEFAULT 'Input multi-actions id here'.
    DATA lo_client TYPE REF TO if_http_client.
    DATA lo_rest_client TYPE REF TO if_rest_client.
    DATA lo_rest_entity TYPE REF TO if_rest_entity.
    DATA lo_request TYPE REF TO if_rest_entity.
    DATA lv_body TYPE string.
    DATA lv_http_status TYPE string.
    DATA lv_response TYPE string.
    DATA lv_acc_token_to_destination TYPE string.
    DATA lv_acc_token_to_sac TYPE string.
    DATA lv_csrf TYPE string.

    获取访问令牌以请求 destination 服务
    相应的 ABAP 实现:
    *----------------------------------------get access to destination service--------------------------------- 
    cl_http_client=>create_by_url( EXPORTING url = '<your token url for the btp destination service>' IMPORTING client = lo_client ).
    CREATE OBJECT lo_rest_client TYPE cl_rest_http_client
    EXPORTING
    io_http_client = lo_client.
    lo_request = lo_rest_client->create_request_entity( ).
    lo_request->set_content_type( iv_media_type = 'application/x-www-form-urlencoded' ).
    lv_body = 'grant_type=client_credentials&client_id=<your oauth client id for destination service>&client_secret=<your oauth client secret for destination service>'.
    lo_request->set_string_data( lv_body ).
    lo_rest_client->post( EXPORTING io_entity = lo_request ).
    lv_http_status = lo_rest_client->get_status( ).
    lo_rest_entity = lo_rest_client->get_response_entity( ).
    lv_response = lo_rest_entity->get_string_data( ).
    DATA lr_data TYPE REF TO data.
    /ui2/cl_json=>deserialize(
    EXPORTING
    json = lv_response
    pretty_name = /ui2/cl_json=>pretty_mode-user
    assoc_arrays = abap_true
    CHANGING
    data = lr_data ).
    IF lr_data IS BOUND.
    ASSIGN lr_data->* TO FIELD-SYMBOL(<lfs_data>).
    ASSIGN COMPONENT 'access_token' OF STRUCTURE <lfs_data> TO FIELD-SYMBOL(<lfs_results>).
    ASSIGN <lfs_results>->* TO FIELD-SYMBOL(<ld_token_value>).
    WRITE: 'Get access to destination service successfully'.
    ENDIF.
    lv_acc_token_to_destination = <ld_token_value>.

    通过 destination 服务请求 destination 以获取访问令牌以访问 SAC
    相应的 ABAP 实现:
    *----------------------------------------get access to destination service--------------------------------- 
    cl_http_client=>create_by_url( EXPORTING url = '<your token url for the btp destination service>' IMPORTING client = lo_client ).
    CREATE OBJECT lo_rest_client TYPE cl_rest_http_client
    EXPORTING
    io_http_client = lo_client.
    lo_request = lo_rest_client->create_request_entity( ).
    lo_request->set_content_type( iv_media_type = 'application/x-www-form-urlencoded' ).
    lv_body = 'grant_type=client_credentials&client_id=<your oauth client id for destination service>&client_secret=<your oauth client secret for destination service>'.
    lo_request->set_string_data( lv_body ).
    lo_rest_client->post( EXPORTING io_entity = lo_request ).
    lv_http_status = lo_rest_client->get_status( ).
    lo_rest_entity = lo_rest_client->get_response_entity( ).
    lv_response = lo_rest_entity->get_string_data( ).
    DATA lr_data TYPE REF TO data.
    /ui2/cl_json=>deserialize(
    EXPORTING
    json = lv_response
    pretty_name = /ui2/cl_json=>pretty_mode-user
    assoc_arrays = abap_true
    CHANGING
    data = lr_data ).
    IF lr_data IS BOUND.
    ASSIGN lr_data->* TO FIELD-SYMBOL(<lfs_data>).
    ASSIGN COMPONENT 'access_token' OF STRUCTURE <lfs_data> TO FIELD-SYMBOL(<lfs_results>).
    ASSIGN <lfs_results>->* TO FIELD-SYMBOL(<ld_token_value>).
    WRITE: 'Get access to destination service successfully'.
    ENDIF.
    lv_acc_token_to_destination = <ld_token_value>.
     获取 SAC 的 csrf 令牌和 session id 
    *----------------------------------------send request get csrf--------------------------------- 
    cl_http_client=>create_by_url( EXPORTING url = '<your SAC url>/api/v1/csrf' IMPORTING client = lo_client ).
    CREATE OBJECT lo_rest_client TYPE cl_rest_http_client
    EXPORTING
    io_http_client = lo_client.
    lo_rest_client->set_request_header(
    EXPORTING
    iv_name = 'X-CSRF-TOKEN'
    iv_value = 'FETCH' ).
    lo_rest_client->set_request_header(
    EXPORTING
    iv_name = 'Authorization'
    iv_value = lv_acc_token_to_sac ).
    lo_rest_client->get( ).
    lv_http_status = lo_rest_client->get_status( ).
    lo_rest_entity = lo_rest_client->get_response_entity( ).
    lv_csrf = lo_rest_entity->get_header_field( 'X-CSRF-TOKEN' ).
    DATA lt_headers TYPE tihttpnvp.
    DATA ls_header TYPE ihttpnvp.
    lt_headers = lo_rest_entity->get_header_fields( ).
    DATA lt_cookies TYPE TABLE OF string.
    DATA lv_cookie TYPE string.
    DATA lv_cookie_remaining TYPE string.
    LOOP AT lt_headers INTO ls_header.
    IF ls_header-name = 'set-cookie'.
    IF ls_header-value CS 'JSESSIONID='.
    SPLIT ls_header-value AT ';' INTO lv_cookie lv_cookie_remaining.
    lv_cookie = lv_cookie+11.
    EXIT.
    ENDIF.
    ENDIF.
    ENDLOOP.
    *WRITE: lv_csrf.
    DATA lv_decoded_cookie TYPE string.
    lv_decoded_cookie = cl_http_utility=>unescape_url(
    escaped = lv_cookie ).
    WRITE: / 'Get csrf token from SAC public api service'.
    *WRITE: lv_csrf.
    *WRITE: lv_decoded_cookie.
    向 SAC 发送请求来开启多步操作 
    *----------------------------------------trigger multiactions--------------------------------- 
    DATA lv_url TYPE string.
    lv_url = '<your SAC url>/api/v1/multiActions/' && pv_ma_id && '/executions'.
    cl_http_client=>create_by_url( EXPORTING url = lv_url IMPORTING client = lo_client ).
    CALL METHOD lo_client->request->set_method( if_http_request=>co_request_method_post ).
    lo_client->request->set_header_field( name = 'x-csrf-token' value = lv_csrf ).
    lo_client->request->set_header_field( name = 'Authorization' value = lv_acc_token_to_sac ).
    lo_client->request->set_header_field( name = 'Content-Type' value = 'application/json' ).
    lo_client->request->set_cookie( name = 'JSESSIONID'
    value = lv_decoded_cookie ).
    lv_body = '{"parameterValues": [ ]}'.
    lo_client->request->set_cdata( data = lv_body ).
    CALL METHOD lo_client->send
    EXCEPTIONS
    http_communication_failure = 1
    http_invalid_state = 2
    http_processing_failed = 3.
    ASSERT sy-subrc = 0.
    CALL METHOD lo_client->receive
    EXCEPTIONS
    http_communication_failure = 1
    http_invalid_state = 2
    http_processing_failed = 3.
    lv_response = lo_client->response->get_cdata( ).
    WRITE: lv_response.
    WRITE: / 'Trigger multi-actions ' && pv_ma_id && ' successfully'.

    在 BTP 中集成 SAP Build Process Automation 


    SAP Build Process Automation(将在下面的描述中使用“BTP PA”来表示该平台)是 BTP 上可用的低代码平台,可用于跨 SAP/非SAP 应用程序构建工作流。(https://www.sap.com/products/technology-platform/process-automation/features.html )我们还可以在 BTP PA 过程中嵌入多步操作 API,以实现相应的业务需求。以下是配置的一些关键步骤: 

    在 BTP 子账户下订阅 SAP Build Process Automation 计划 


    在 SAC 中创建一个 OAuth Client,就像上面的 ABAP/Netweaver 一节中提到的一样。 
    使用 OAuth2 SAML Bearer Assertion 认证类型在 BTP 中创建 destination,就像上面 ABAP/Netweaver 部分中提到的一样,同时也有一些需要注意的差异: 
  • 在 URL 后增加“api/v1”后缀,例如:https://{tenant url}/api/v1 

  • 在 additional properties 部分,增加: 

  • 创建一个多步操作 API action 
    进入“lobby”选项卡,点击“create”按钮,选择“build a automated process”,然后选择“actions”。 
    点击“upload API specification”,上传多步操作 API 规范 
  • 你可以从 https://api.sap.com/api/MultiActions_API/overview 下载 API 规范。由于我们的destination URL 被定义为 https://{tenant url}/api/v1,而不是 API 规范中定义的https://{tenant url}/api/v1/multiActions(因为 BTP PA 需要在相同的 destination url 前缀下配置 csrf 令牌 url),让我们手动更新 API 规范中的路径名,以添加前缀“/multiActions”。 

  • SAP Integration Suite: download XML from dynamic SFTP path (step Sender) in Technology Q&A How to put fiori app check just like SY-TCODE check in sap backend enhancement in Technology Q&A Execute and Stop a job using command in Technology Q&A SAP UI5 Annotations Advantages with Json Model in Technology Q&A