Skip to Content
Technical Articles
Author's profile photo Yohei Fukuhara

CFでApp Router, XSUAAとJava Application開発(SAP Cloud SDK使用)

App RouterとXSUAA を使ったJava Application開発を試してみました。以下の図のアーキテクチャを作ります。

チュートリアル「Secure Your Application on SAP Cloud Platform Cloud Foundry」を参考にしています。複数のサービスを使うので、理解にしにくく時間がかかりました。

 

開発環境

開発環境はJavaとNode.jsで分けています。既存のJava開発環境がWindowsにあったため継続して使っているだけで、深い理由はないです。

Java開発環境(Windows)

Javaは以下の環境で実行しています。

  • OS: Windows10 64-bit
  • openJDK: 1.8.0_242
  • Chocolatey: 0.10.15
  • maven: 3.6.3
  • IDE: IntelliJ IDEA Community Edition 2019.3.3
  • CF cli: 6.51.0+2acd15650.2020-04-07
  • SAP Cloud SDK for Java: 3.18.1

※ CF cliは最新にしておきましょう。古いバージョンだと、manifest.ymlの情報が”cf push”で正しく送られないことがあるようです。このせいで半日ほど試行錯誤しました。

Node.JS開発環境(Ubuntu)

Node.jsは以下の環境で実行しています。

  • OS: Ubuntu18.04.01 LTS
  • nvm: 0.35.3
  • Node.js: 12.16.2
  • npm: 6.14.4
  • SAP Cloud SDK for JavaScript:1.19.0
  • SAP Cloud SDK cli: 0.1.8
  • nest cli: 7.1.2
  • CF cli: 6.51.0+2acd15650.2020-04-07

手順

1. Java Application作成

Windows環境で実行しています。

プロジェクトを置くディレクトリで以下のコマンドでプロジェクト作成(コマンド実行でフォルダが生成されます)。

mvn archetype:generate "-DarchetypeGroupId=com.sap.cloud.sdk.archetypes" "-DarchetypeArtifactId=scp-cf-tomee" "-DarchetypeVersion=RELEASE"

途中のプロンプトでは以下を入力。”artifactId”に入力した”test-sec”がApplication名です。

  • groupId: com.sap.cloud.sdk
  • artifactId: test-sec
  • version: 1.0-SNAPSHOT
  • package: com.sap.cloud.sdk

パッケージ生成

mvn clean package

ディレクトリを移動してtomee起動。

cd application && mvn tomee:run

ブラウザで”localhost:8080/hello”を開くと”Hello World!”が表示されます。

tomeeを止めて、プロジェクトのルートディレクトリに戻り、CFにデプロイします(CFにはログイン済み)。

cd .. && cf push

デプロイに成功したらブラウザでhttps://<host.domain>/hello”を開いて確認。

2. XSUAAとApp Router作成

Ubuntuで実行。

2.1. cliでApp Routerをローカルに作成

SAP Cloud SDK for Javascriptのcliを使ってApp Routerを作成します。

作成したいディレクトリに移動して以下のコマンド実行。

$ sap-cloud-sdk add-approuter
No 'manifest.yml' found.
Enter project name as maintained in Cloud Foundry: test-sec
   Creating files
 Successfully added approuter to your project.

Generated files might need customization. Documentation available here:
- xs-security.json (for help check https://help.sap.com/viewer/4505d0bdaf4948449b7f7379d24d0f0d/2.0.02/en-US/e6fc90df44464a29952e1c2c36dd9861.html)
- xs-app.json (for help check https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/c103fb414988447ead2023f768096dcc.html)
- mainfest.yml (for help check https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/ba527058dc4d423a9e0a69ecc67f4593.html)

途中のプロンプトではJava Applicationの名前”test-sec”を入力。

これで、固定名”approuter”というディレクトリが生成され、その中にApp Routerの内容が詰っています。lsコマンドで確認。

$ ls approuter -al
total 28
drwxr-xr-x  2 i348221 i348221 4096 Apr 30 11:46 .
drwxr-xr-x 14 i348221 i348221 4096 Apr 30 11:46 ..
-rw-r--r--  1 i348221 i348221  448 Apr 30 11:46 manifest.yml
-rw-r--r--  1 i348221 i348221   87 Apr 30 11:46 .npmrc
-rw-r--r--  1 i348221 i348221  158 Apr 30 11:46 package.json
-rw-r--r--  1 i348221 i348221  123 Apr 30 11:46 xs-app.json
-rw-r--r--  1 i348221 i348221   57 Apr 30 11:46 xs-security.json

※.npmrcで変な値が設定され、AppRouterをCFにデプロイ時にエラーが起きたことがありました。.npmrcファイルの中身は以下であるべきです。

@sap:registry=https://npm.sap.com/

 

2.2. XSUAAインスタンスを作成

cliで生成された”xs-security.json”を変更します。

今回は暫定的にSingle Tenant にするので、”tenant-mode”をshared から dedicatedに変更。詳しくはチュートリアルヘルプ文書などを参照。sharedだとXSUAAサービスがグローバルで作られてしまい、うまく認証ができなかったのでdedicatedにしています(どちらにすべきかは調べていないです)。

{
  "xsappname": "test-sec",
  "tenant-mode": "dedicated"
}

“xs-security.json”を使ってXSUAAインスタンスを作成。

cf create-service xsuaa application test-sec-xsuaa -c xs-security.json

cf cliでサービスを確認。

$ cf services
name             service      plan          bound apps   last operation     broker                                                      upgrade available
abap             abap-trial   shared                     create succeeded   sm-abap-trial-broker-3e22f640-a893-497a-a56a-01d090a4cbb7   
test-sec-xsuaa   xsuaa        application                create succeeded   sm-xsuaa-9ef36350-f975-4194-a399-54db361e79b5             
$ cf service test-sec-xsuaa

name:             test-sec-xsuaa
service:          xsuaa
tags:             
plan:             application
description:      Manage application authorizations and trust to identity providers.
documentation:    https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/6373bb7a96114d619bfdfdc6f505d1b9.html
dashboard:        
service broker:   sm-xsuaa-9ef36350-f975-4194-a399-54db361e79b5

Showing status of last operation from service test-sec-xsuaa...

status:    create succeeded
message:   
started:   2020-04-30T02:59:02Z
updated:   2020-04-30T02:59:02Z

There are no bound apps for this service.

XSUAAをJava Applicationにbindします。

cf bind-service test-sec test-sec-xsuaa

既にXSUAAサービスがある場合は、上記のタイミングでxs-security.jsonを使います。

cf bind-service test-sec test-sec-xsuaa -c xs-security.json

 

bindを反映させるため、restageしておきます。これでJava applicationとXSUAAインスタンスが繋がりました。

cf restage test-sec

2.3. App Router用Route作成

App Router用のRouteを作成します。

“dev”の部分はcfのスペース名です。

cf create-route dev cfapps.eu10.hana.ondemand.com --hostname <App Routerのホスト名>

2.4. App Router作成

次にApp Routerの”manifest.yml”を更新します。

env -> destinationsのurlはステップ1で作成したJava Applicationのエンドポイントです。

applications:
  - name: test-sec-approuter
    routes:
      - route:
          https://<2.3で作成したRouteのホスト名>.<2.3で作成したRouteのドメイン名>
    path: .
    memory: 128M
    buildpacks:
      - nodejs_buildpack
    env:
#      TENANT_HOST_PATTERN: >-
#        "test-sec-(.*).cfapps.sap.hana.ondemand.com"
      destinations: >-
        [{"name":"test-sec","url":"https://<1で作成したJavaのホスト・ドメイン名>","forwardAuthToken":true}]
    services:
      - test-sec-xsuaa

※YAMLの規則で”>-“を使うと改行をスペースに置換するようです。”-“は最終行の改行も削除という意味らしい。

cf pushします。

cf push

これで、XSUAA, App Router, Java Applicationが繋がりました。

App Routerのエンドポイント”https://<2.3.で登録したRouteのエンドポイント>/hello”をブラウザで開くと認証画面へリダイレクトされ、認証完了するとJava Application画面が開きます。

しかし、この時点ではまだ直接Java Applicationのエンドポイントを認証なしで開くことができます。

3. Java Applicationにセキュリティ実装

WIndowsで実行

3.1. manifest.yml変更

“manifest.yml”を変更しておきます。

Routeがランダムだったものを固定にし、XSUAAサービスと紐付けます。

---
applications:

- name: test-sec
  memory: 1024M
  timeout: 300
  random-route: false
  routes:
    - route:
        https://<1でランダムに登録されたRoute>  
  path: application/target/test-sec-application.war
  buildpacks:
    - sap_java_buildpack
  env:
    TARGET_RUNTIME: tomee7
    SET_LOGGING_LEVEL: '{ROOT: INFO, com.sap.cloud.sdk: INFO}'
    JBP_CONFIG_SAPJVM_MEMORY_SIZES: 'metaspace:128m..'
  services:
#  - my-application-logs
  - test-sec-xsuaa
#  - my-destination
#  - my-connectivity

3.2. web.xml変更

“application/src/main/webapp/WEB-INF/web.xml”を変更します。SAP Cloud SDK for Javaを使うとコメントアウトされている以下の部分があり、その箇所を有効にします。

    <login-config>
        <auth-method>XSUAA</auth-method>
    </login-config>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Baseline Security</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>*</role-name>
        </auth-constraint>
    </security-constraint>

    <security-role>
        <role-name>Display</role-name>
    </security-role>

この状態でパッケージ生成をすると、サーブレットにアクセスできません。直接Java Applicationにアクセスすると401(Unauthorized)エラー。App Router経由でユーザ認証してアクセスしても403(Forbiden)エラーとなります。

ちなみに<login-config>部分だけを有効にした場合は、直接Java Applicationにアクセスできます。

3.3. サーブレット変更

サーブレットでデコレータ””@ServletSecurity”を使ってセキュリティの実装をします。今回は、SAP Cloud SDK for Javaで最初に生成される”HelloWorldServlet.java”をそのまま使っています(ディレクトリ”application/src/main/java/com/sap/cloud/sdk”)。

ソース全体は以下の通りです。

package com.sap.cloud.sdk;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.annotation.HttpConstraint;
import javax.servlet.annotation.ServletSecurity;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/hello")
@ServletSecurity(@HttpConstraint(rolesAllowed = { "Display" }))
public class HelloWorldServlet extends HttpServlet
{
    private static final long serialVersionUID = 1L;
    private static final Logger logger = LoggerFactory.getLogger(HelloWorldServlet.class);

    @Override
    protected void doGet( final HttpServletRequest request, final HttpServletResponse response )
        throws IOException
    {
        logger.info("I am running!");
        response.getWriter().write("Hello World!");
    }
}

3.4. CFデプロイ

あとはパッケージ生成してCloud Foundryにデプロイします。

これで、直接Java Applicationにアクセスすると401(Unauthorized)エラー。App Router経由でユーザ認証してアクセスしても403(Forbiden)エラーとなります。

# From Application Directory
mvn clean package

# move to Root Directory and push
cd .. && cf push

4. ロール定義

4.1. XSUAA更新

Ubuntuで実行。

XSUAAにscopesとrole-templatesの情報を追加します。追加対象のファイルは”xs-security.json”です。

「2.2. XSUAAインスタンスを作成」のタイミングで追加していても大丈夫です。

“role-collections”を作れば、後続の「4.2.Role Collection追加」が不要でした(2020/8/26追記)。

{
  "xsappname": "test-sec",
  "tenant-mode": "dedicated",
  "scopes": [
    {
      "name": "$XSAPPNAME.Display",
      "description": "display"
    }
  ],
  "role-templates": [
    {
      "name": "Viewer",
      "description": "Required to view things in our solution",
      "scope-references"     : [
        "$XSAPPNAME.Display"
      ]
    }
  ]
}

これでXSUAAサービスをアップデートします。

cf update-service test-sec-xsuaa -c xs-security.json

4.2. Role Collection追加

SAP Cloud Platform CockpitからRole Collectionを追加します。Subaccountを開いて、メニューからSecurity -> Role Collectionsで”New Role Collection”ボタンを押します。

 

NameとDescriptionを入力して保存。

4.3. Role追加

先のステップで追加したRole Collection詳細画面で”Add Role”ボタンを押します。

Application IdentifierにJava Applicationを選択(“!”以降は何を表しているかわかりませんでしたが、GUIDみたいなものと推測)。

Role TemplateとRoleを選択(ステップ「4.1. XSUAA更新」で追加したRole Templateが表示されます)。

4.4. Role Collectionのユーザ割当

SAP Cloud Platform CockpitからRole Collectionをユーザに割り当てます。

Subaccountを開いて、メニューからSecurity -> Trust Configurationでデフォルト設定を選択。

割当したいユーザを選択して「Show Assignments」ボタンを押します。

「Assign Role Collection」ボタンを押します。

ステップ「4.2. Role Collection追加」で登録したRole Collectionを選びます。

 

これでApp Router経由だとアクセスできるようになり、直接Java Applicationはアクセスできなくなりました(401 Unauthorized)!

App Rotuerのエンドポイントにアクセスすると以下の順序でJava Applicationに繋がります。

  1. App Routerのmanifest.ymlで指定したroute
  2. App RouterがbindしているXUSAAサービスで認証し、Tokenを受け取る
  3. App Routerのxs-app.jsonに記載されているsourceパスからtargetとdestinationを取得
  4. App Routerのmanifest.ymlに記述した環境変数のdestinationに対応するurlへ遷移(多分Destinationサービスを見に行く形でもOk)

Assigned tags

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