Skip to Content
Technical Articles
Author's profile photo Osamu Tamaki

SAP Launchpad Service での動的タイル実現方法 (2/3) – Cloud Foundry上のFioriアプリケーションを動的タイルとして表示する

本ブログは、SAP Launchpadサービスの概要について に関連する連載記事の第2回となります。

  1. SAP S/4HANAのFioriアプリケーションを動的タイルとして表示する
  2. Cloud Foundry上のFioriアプリケーションを動的タイルとして表示する← ★ 本ブログ
  3. (仮題) Cloud Foundry上のFioriアプリケーションをカスタムビジュアライゼーションタイルとして表示する

本ブログ(第2回) では、Cloud Foundry上にDeployしたFioriアプリケーション(=SAPUI5アプリケーション)に、これまたCloud Foundry上にDeployしたJava applicationをデータソースとして、SAP Launchpad Serviceでパーソナライズされた数値を表示するタイル(動的タイル = Dynamic Tile) を作成するシナリオの実現方法を記載します。

Index

  1. はじめに
  2. 前提
  3. 実現ステップ
    1. Java Applicationの実装
    2. XSUAAサービスの作成
    3. Destinationの作成
    4. 動的タイルの作成
  4. 終わりに

 

1. はじめに

本ブログでは主にCloud Foundry上にDeployされたSAPUI5アプリケーションをSAP Launchpad Serviceより呼び出す際の動的タイル作成方法について説明します。具体的なアーキテクチャイメージは以下のようになります。

 

<<図1. アーキテクチャ>>
testtest
  1. SAP Launchpad Serviceにアクセスしタイルが表示されます
  2. SAP Lauhchpad Serviceにログオンしたユーザーを使用してJava ApplicationのAPIをコールし、動的タイルに表示する用のパーソナライズされた数字を取得します
  3. タイルをクリックするとSAPUI5アプリケーションが呼ばれます
勘の良い読者の方は気づくかもしれませんがこの実装の肝はSingle Sign Onです。SAP Launchpad ServiceでログオンしたユーザーコンテキストをどのようにJava Applicationに引き継ぐのかが実装のポイントです。<<図1. アーキテクチャ>>に認証のポイントを追加したイメージが以下になります。このイメージを持って本ブログを読み進めて頂ければ理解も深まるかと思います。

 

図2. 認証イメージ
  1. SAP Launchhpad Serviceログイン時の認証フローです。XSUAAとOAuth Authorization Code Flowを使用してログオンします。
  2. Java Application呼び出し時の認証フローです。Destination経由でXSUAAとOAuth JWT Bearer Token Flowを使用してJava Applicationに対してSSOします。
なお連載記事の1回目で動的タイルに詳細情報を表示するODataの構造については既に説明済みです。本ブログではあくまでSSOに焦点を当て、Java Applicationは簡単な数字のみを返すWeb APIの実装(Spring BootのRestController)としており、完成系のタイルイメージは以下のようになります。

見栄えの良いタイルを作成したい場合は連載記事の1回目で説明した通り以下サイトに説明のあるODataの構造に準拠したAPIを作成してください。
それでは具体的な実装例についてStep By Stepで説明していきます。
 

2. 前提

実装に入る前に3点ほどテクニカルな前提があります。

  1. SAPUI5アプリケーションをSAP Launchpad Serviceと統合するためにはHTML5 Content providerとしてSAPUI5アプリケーションをデプロイする必要があります。従来のStandalone版Application routerを使用したデプロイ方法とは異なるので注意が必要です。動的タイルの話とは直接は関連しないので詳細について説明は本ブログではしませんが、興味のある方は以下サイトにてContent providerとするSAPUI5アプリケーションのデプロイ方法についてご確認ください。https://help.sap.com/viewer/24284cbd872f45a4bfa2f51c37cc7063/Cloud/ja-JP/3a0e6d6b791c4c2189f6a0a424188362.html
  2. SAP Launchpad ServiceとSAPUI5アプリケーション、Java Applicationは全て同じサブアカウントに配備することを想定としています。基本的にXSUAAのToken(JWT)はサブアカウント内でのみ伝播が可能です。異なるサブアカウントにまたがるApplication連携は別途SSOの設計が必要になります。
  3. 作成するJava ApplicationはSpring BootのRestControllerの実装とします。また認証/認可実装はSpring Security、ビルドツールはMavenを使用することとします。

 

3. 実現ステップ

3-1. Java Applicationの実装
まず最初に動的タイルに数字を返すJava Applicationの作成方法について説明します。前述したようにJava Application自体はSpring BootのRestControllerベースとなります。
<<図2. 認証イメージ>>で説明した通りSAP Launchpad ServiceにログオンしたユーザーコンテキストはOAuthのAccess Tokenという形でJava Applicationに到達します。そのためJava Application側はOAuthで認証/認可を可能とする仕組みを実装する必要があります。XSUAAをOAuth Serverとする場合SAPが提供するSpring SecurityのOAuth認証実装ライブラリを使用するのが一般的です。そのためお手持ちのpom.xmlに以下モジュールをDependencyに追加します。
<dependency>
    <groupId>com.sap.cloud.security.xsuaa</groupId>
    <artifactId>xsuaa-spring-boot-starter</artifactId>
    <version>${sap.cloud.security.version}</version>
</dependency>

 

またSpring SecurityのConfigurationクラスに以下のような実装を追加します。
@Override
protected void configure(HttpSecurity http) throws Exception {
    // @formatter:off
    http
        .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // session is created by approuter
        .and()
            .authorizeRequests()
            .antMatchers("/v1/sayHello").hasAuthority("Read")
            .antMatchers("/v1/*").authenticated()
            .antMatchers("/v2/*").hasAuthority("Read")
            .antMatchers("/v3/*").hasAuthority("Read")
            .antMatchers("/v3/requestRefreshToken/*").hasAuthority("Read")
            .antMatchers("/health").permitAll()
            .antMatchers("/tokencheck").permitAll()
            .antMatchers("/jwtcheck").permitAll()
            .antMatchers("/gettestnum").permitAll()
            .antMatchers("/").permitAll()                                             
            .anyRequest().denyAll()
        .and()
            .oauth2ResourceServer()
            .bearerTokenResolver(new IasXsuaaExchangeBroker(xsuaaTokenFlows))
            .jwt()
            .jwtAuthenticationConverter(getJwtAuthenticationConverter());
}

/**
* Customizes how GrantedAuthority are derived from a Jwt
*/
Converter<Jwt, AbstractAuthenticationToken> getJwtAuthenticationConverter() {
    TokenAuthenticationConverter converter = new TokenAuthenticationConverter(xsuaaServiceConfiguration);
    converter.setLocalScopeAsAuthorities(true);
    return converter;
}

 

上記実装によりOAuthの認証/認可、および、特に後半”.and”以降の実装にてOAuthのAccess tokenがThreadLocalに格納され、RestControllerよりAccess Tokenが使用可能になります。
それでは次にRestControllerの実装について説明します。
@GetMapping("/gettestnum")
public int gettestNum(@AuthenticationPrincipal Token token) {
    System.out.println("Come here!!!");
    if (token == null) {
        System.out.println("token is null.....");
        return 0;
    }
    System.out.println("Got the Xsuaa token: " + token.getAppToken());
    System.out.println("grant type: " + token.getGrantType());
    System.out.println("client id: " + token.getClientId());
    System.out.println("subaccount id: " + token.getSubaccountId());
    System.out.println("zone id: " + token.getZoneId());
    System.out.println("logon name: " + token.getLogonName());
    System.out.println("family name: " + token.getFamilyName());
    System.out.println("given name: " + token.getGivenName());
    System.out.println("email: " + token.getEmail());
    System.out.println("authorities: " + String.valueOf(token.getAuthorities()));
    System.out.println("scopes: " + String.valueOf(token.getScopes()));
    //
    // 色々なJWTのAttributeで処理を分岐
    //
    return 3;
}

 

こちらの実装でFramework内に格納したAccess TokenをMethodの引数にInjectしてRestContoroller内で使用します。この実装では実際ではあり得ない実装ですがTokenがない場合(=OAuth serverとやりとりせずに直にAPIが呼ばれる場合)0を返し、それ以外では3を返します。実際にはTokenがない場合はConfigurationファイルにてアクセス不可設定をし、Access Tokenの中身によりパーソナライズした数字情報をSAP Launchpad Service側に返す実装になります。なおこちらの標準出力の実装を見ればTokenに何が含まれていて、どのようにパーソナライズしたハンドリングが可能かイメージがつくかと思います。例えばメールアドレスをキーにデータをアグリゲートして値を返すような実装が可能です。
なおAccess TokenにDefaultで含まれない属性をパーソナライズする際に使用したい場合、IASの任意の値をAccess Tokenに含めることも可能です。その場合XSUAAサービスを作成する際、xs-security.jsonに追加属性を指定する形になりますが詳細な実装はここでは触れずにおきます。これでJava Applicationの実装は終了です。

 

3-2. XSUAAの作成
次にXSUAAサービスを作成する必要があります。この作業はOAuthアプリケーションをOAuthサーバーに登録するために必要な作業です。今回はXSUAAを手動で作成し、manifest.yamlを使用してBindする形をとります。
まずはxs-security.jsonを以下のように作成します。
{
  "xsappname": "spring-security-xsuaa-usage",
  "oauth2-configuration": {
    "credential-types": ["x509"]
  },
  "tenant-mode": "dedicated",
  "scopes": [
    {
      "name": "$XSAPPNAME.Read",
      "description": "Read Permissions."
    },
    {
      "name": "$XSAPPNAME.Admin",
      "description": "Admin permissions."
    }
  ],
  "role-templates": [
    {
      "name": "Viewer",
      "description": "View Data",
      "scope-references": [
        "$XSAPPNAME.Read",
        "uaa.user"
      ]
    },
    {
      "name": "Administrator",
      "description": "View Sensitive Data",
      "scope-references": [
        "$XSAPPNAME.Read",
        "$XSAPPNAME.Admin"
      ]
    }
  ],
  "role-collections": [
    {
      "name": "Viewer",
      "description": "Viewer (read)",
      "role-template-references": [
        "$XSAPPNAME.Viewer"
      ]
    },
    {
      "name": "Administrator",
      "description": "Administrator (read all)",
      "role-template-references": [
        "$XSAPPNAME.Administrator"
      ]
    }
  ]
}​
 

今回はViewerとAdministratorのRole templateを作成しますが、こちらについては要件に合わせて自由に作成してください。重要なポイントは”uaa.user”をRole templateに含め、必ずユーザーにアサインする点です。このxs-security.jsonを使用してマニュアルでXSUAAをPlan”Application”で作成します。
最後に作成したXSUAAサービスをバインドする形で上記Java ApplicationをCloud FoundryにPushします。具体的には下記manifest.yamlを使用してcf pushします。
---
applications:
- name: otaspringsecuritytest
  buildpack: sap_java_buildpack
  path: ./target/spring-security-xsuaa-usage.jar
services:
  - ota_springsecurity_test_xsuaa

 
3-3. Destinationの作成
次はDestinatioの作成です。この作業は<<図2. 認証イメージ>>で示したように、上記で説明したJava ApplicationがBTPのDestinationサービス経由でAccess Tokenを取得するために必要です。このフローは本ブログの中核なので、<<図2. 認証イメージ>>をより詳細化した以下の図を用いて説明します。

 

図3. 認証シーケンス
以下連番は全て#2の詳細番号です。
  1. SAP Launchpad Serviceログイン時にXSUAAより取得したAccess TokenをDestinationに渡します。
  2. DestinationサービスはOAuth JWT Bearer Token Flowに則り#2-1で受け取ったAccess Tokenを使用してXSUAAのToken EndpointをCallします。
  3. 受け取ったAccess Tokenの妥当性を判断し問題ない場合、上記したJava Application用のAccess Tokenを返します。このAccess TokenにJava ApplicationのOAuthアプリケーション情報が含まれています。この情報はこれまた上記で作成したXSUAAサービスの登録情報そのものです。
  4. Destinationサービスは取得したJava Application用のAccess TokenをManaged Application Routerに返します。
  5. 最後にManaged Application Routerは取得した新しいAccess Tokenを使用しJava Application APIをCallします。
これらOAuth JWT Bearer Token Flowの複雑なシーケンスを、Destinationサービスを作成するだけで、Managed Application Routerが実行してくれる形になります。
それでは実際にDestinationサービスを作成していきましょう。まずはDeployしたJava ApplicationのEnvironment Variablesを開きます。その中のxsuaa属性の中身が重要なのでコピーします。コピーした内容をDestinationサービスを作成する際使用する形になりますが、コピーした内容とDestinationサービス設定項目のマッピングは以下のようになります。
マッピング項目以外は以下を参考にしてください。
Properties Name
任意の名前。”_”は使用不可
Type
HTTP
Description
<Option>
URL
作成済みJava ApplicationのURL
Proxy
Internet
Authentication
OAuth2UserTokenExchangeもしくはOAuth2JWTBearer
Client ID
Environment variableからコピー
Client Secret
Environment variableからコピー
Token Service URL Type
Dedicated
Token Service URL
Environment variableからコピー + 末尾に/oauth/token
Additional Properties HTML5.DynamicDestination true

3.4. 動的タイルの作成

最後にタイルを作成します。OnPremiseのFiori Launchpadと異なり、Cloud Foundry上にDeployされたSAPUI5アプリケーションをSAP Launchpad Serviceより呼び出す際の動的タイルは、SAPUI5のmanifest.jsonに指定します。セマンティックオブジェクト、アクション、タイトル、アイコンなどOnPremiseのFiori LaunchpadではLaunchpad Designer側で設定した値が全てmanifest.json側で指定するのでご注意ください。実際の設定はsap.app配下に以下のように記述します。
"crossNavigation": {
    "inbounds": {
        "intent1": {
            "signature": {
                "parameters": {},
                "additionalParameters": "allowed"
            },
            "semanticObject": "Object",
            "action": "otadisplay",
            "title": "Ota Dynamic Tile!",
            "info": "{{appTitle}}",
            "subTitle": "{{appSubTitle}}",
            "icon": "sap-icon://account",
            "indicatorDataSource": {
                "dataSource": "otaspringsecuritytest",
                "path": "/dynamic_dest/otaspringsecuritytest/gettestnum",
                "refresh": 0
            }
        }
    }
}

 

dataSourceに上記で作成したDestinationサービス名、pathにはDestinationサービスで指定したJava ApplicationのURLに加えて、実際のEndpointのPathを指定する形になります。その際必ず/dynamic_destをpathの先頭に含めることを忘れないでください。奇妙な感じもしますがこれはSAP Launchpad Serviceの仕様です。
このSAPUI5をCloud Foundry環境にDeployするとSAP Launchpad Serviceのコンテンツマネージャで以下のように値が反映されていることが確認できます。
後はこのSAPUI5アプリケーションをSAP Launchpad ServiceのGroup/Catalog/Roleに割り当てれば完成です。

 

4. 終わりに

繰り返しになりますが肝はManaged Application Routerを中心としたOAuth JWT Bearer Flowの取り回しです。同じサブアカウントにて認証を行う際、同じXSUAAを使用するためコンポーネント間のAccess Tokenの引き回しが簡単に行えます。これは今回の例で説明した動的タイルに限らない話です。サブドメイン内のApplication to Applicationの呼び出しが必要な場合にも応用が効く話なので是非覚えておいてください。それでは楽しいSAP開発ライフを!

Assigned Tags

      Be the first to leave a comment
      You must be Logged on to comment or reply to a post.