SaaSus SDK をWebアプリケーションに組み込みマルチテナント化する

WebアプリケーションにSaaSus SDKを組み込む

※ここからはプログラミングの知識が必要になります

SaaS ID と API キー、クライアントシークレットの再確認

まず、SaaSus 開発コンソールにてAPIキーを表示しておきましょう。これをアプリケーションの設定に使います
(API キーは決して外部に漏らさないようにお気をつけください。このチュートリアルに表示されている API キーは既に無効化してあります)

SaaSus SDK 利用の準備

では、さきほど準備したサンプルアプリケーションを開発環境で開きましょう。

冒頭で行ったとおり、 init.sh を利用して docker コンテナが立ち上がっており、http://localhost/board でサンプルアプリケーションが動いていることを確認しておいてください。

まず、 SaaSus Platform を簡単に使うためには、 SaaSus SDK をアプリケーションに組み込みます。

今回はPHPなので、 composer を使います。

ターミナルを開きphpのコンテナに入り、composerを使ってセットアップをします。

repo$ docker compose exec php bash
root@xxx:/var/www# cd api
root@xxx:/var/www/api# composer config repositories.saasus-platform/saasus-sdk-php vcs https://github.com/saasus-platform/saasus-sdk-php

api ディレクトリで、 composer コマンドを使って追加します

root@xxx:/var/www/api# composer require saasus-platform/saasus-sdk-php

SaaSus SDKがインストールできたら、まずは SaaSus SDK 利用にあたって必要な環境変数を定義しましょう

apiディレクトリの .env.exampleファイルをコピーして.envファイルを作成し、その.env ファイルの一番下の以下の部分を編集します

### for SaaSus Platform
SAASUS_SAAS_ID="98tjo3wifaoua (画面のSaaS ID)"
SAASUS_API_KEY="kjntowjfoasnkjntwonsflajsno4as (画面のAPI KEY)"
SAASUS_SECRET_KEY=" (画面のクライアントシークレット)"
SAASUS_LOGIN_URL="https://auth.sample.saasus.jp/ (ログイン画面のURL)"

SAASUS_SAAS_ID, SAASUS_API_KEY, SAASUS_SECRET_KEY は先ほど画面で表示した SaaS ID と API Key、クライアントシークレットを、
SAASUS_LOGIN_URL は、SaaSus 開発コンソールで作成したログイン画面のURLを設定します。

認証モジュールの組み込み

次に、 SaaSus Platform の認証モジュールを組み込みます。

今回のアプリケーションでは、すべてのURIのルートに認証を必要とします。認証されていない場合には、アプリケーションが利用できない仕様にします。そのため、 Laravel の Middleware としてSaaSus認証機能を組み込みます。

現状は、

api/routes/web.php

にて、Laravel標準の認証機能が利用されているので、SaaSusの認証機能に置き換えます。

この部分

Route::middleware('auth')->group(function () {
   Route::get('/', function () {
       return view('welcome');
   });
   Route::get('/dispatch', 'App\Http\Controllers\DispatchController@index')->name('dispatch');
   Route::get('/board', 'App\Http\Controllers\MessageController@index')->name('board');
   Route::post('/post', 'App\Http\Controllers\MessageController@post')->name('post');
});

require __DIR__ . '/auth.php';

// Route::middleware('auth')->group(function () {
//     Route::get('/', function () {
//         return view('welcome');
//     });
// SaaSus SDK標準のAuth Middlewareを利用する
Route::middleware(\AntiPatternInc\Saasus\Laravel\Middleware\Auth::class)->group(function () {
   Route::get('/dispatch', 'App\Http\Controllers\DispatchController@index')->name('dispatch');
   Route::get('/board', 'App\Http\Controllers\MessageController@index')->name('board');
   Route::post('/post', 'App\Http\Controllers\MessageController@post')->name('post');

   Route::redirect('/', '/board');
});

// require __DIR__ . '/auth.php';

このようにします。

そして、認証画面からのコールバックの受口を用意します。先ほどSaaSus 開発コンソールでコールバック先を http://localhost/callback と定義したので、 /callback で受け取れるようにします。

おなじく、 api/routes/web.php の最後の行に以下を追加します。

// SaaSus SDK標準のCallback Controllerを利用して、JWTをCookieやLocal Storageに入れる
Route::get('/callback', 'AntiPatternInc\Saasus\Laravel\Controllers\CallbackController@index');

さらに、SaaSus SDKが提供するViewを使えるように api/config/view.php にパスを追加します

   'paths' => [
       resource_path('views'),
       # ↓この行を追加:SaaSus SDKが提供するViewのディレクトリ
       resource_path('../vendor/saasus-platform/saasus-sdk-php/src/Laravel/Views'),
   ],

ここまで設定すると、 アプリケーションの Controller にたどり着いた時点で、SaaSus Platformで設定した認証情報がrequestの一部として渡ってくるようになります。

api/app/Http/Controllers/MessageController.php の index に Request の引数を追加し、 dd を使って $request に userinfo が入っているか確認してみましょう

以下の4行を書き換えてください。

   public function index(Request $request)
   {
       // SaaSus Platformからユーザ情報が渡ってきているかを確認する
       dd($request->userinfo);

ここまでで連携の基本ができました。

では、実際にSaaSus Platformからログインして動作確認してみましょう。

SaaSus SDK 組み込みの確認

SaaSus Platform で作成したログイン画面を表示します。

https://auth.sample.saasus.jp など、ご自身で設定したドメインのログイン画面を表示してください。

先ほど作成したユーザのメールアドレスとパスワードでログインすると、Callback URLで設定したURLに、認証情報とともにリダイレクトされます。

たとえば、 [email protected] でログインしてみましょう。

実際に先ほどのコードが動作すると、ログイン後にこのような表示になるはずです。

array:3 [▼
  "email" => "[email protected]"
  "id" => "f6a02019-1306-431f-b93d-3a756b312481"
  "tenants" => array:1 [▼
    0 => array:7 [▼
      "back_office_staff_email" => "[email protected]"
      "completed_sign_up" => true
      "envs" => array:1 [▼
        0 => array:3 [▼
          "id" => 1
          "name" => "dev"
          "roles" => array:1 [▼
            0 => array:2 [▼
              "display_name" => "一般利用者"
              "role_name" => "user"
            ]
          ]
        ]
      ]
      "id" => "7b639774-6fba-4b26-b580-f3d755876a4b"
      "name" => "テナントのサンプルその1"
      "plan_id" => "bc011444-a9f1-41c0-8251-bc8928b09ee7"
      "user_attribute" => array:1 [▼
        "username" => "user1-1"
      ]
    ]
  ]
]

アプリケーション側で、先ほど SaaSus Platform で設定したユーザ情報、テナント情報が取得できているのがわかります。

リダイレクト先のURLは、今回 SaaSus SDK 標準の Callback 処理で受けるようになっており(http://localhost/callback)、その処理の中で ブラウザの Local Storage や Cookie の中に認証情報を記憶します。

そして、 SaaSus SDK の Auth Middleware で、SaaSus Platformを利用し認証情報を検証しユーザ情報を取得して Request オブジェクトに詰めます。

そのあと、アプリケーションのコントローラに処理が移るので、この時点ですでにアプリケーションはログインした人の情報を持っていることになります。

では、この情報を使って、掲示板アプリケーションをマルチテナント対応にしてみましょう!

サンプルアプリケーションのマルチテナント化

api/app/Http/Controllers/MessageController.php がメインの処理なので、ここにマルチテナント対応にするための処理を入れてみましょう。

まず、表示の部分を変更します。
public function index の部分をまるごと書き換えてみましょう。

   public function index(Request $request)
   {
       // $request->userinfo に各種ユーザ情報、テナント情報が入ってくるので、それを使う
       $messages = Message::where('tenant_id', $request->userinfo['tenants'][0]['id'])->get();
       return view('messageBoard.index', ['messages' => $messages, 'plans' => $this::PLANS, 'tenant_name' => $request->userinfo['tenants'][0]['name']]);
   }

このようにして、渡ってきたテナントIDをもとにDBを検索するようにします。

次に投稿の部分です。

   public function post(Request $request)
   {
       $validated = $request->validate([
           'message' => 'required|max:255'
       ]);

       // $request の userinfo から各種情報を取得し、判断に使う
       $message = Message::create([
            'tenant_id' => $request->userinfo['tenants'][0]['id'],
            'user_id' => $request->userinfo['tenants'][0]['user_attribute']['username'],
            'message' => $request->message,
        ]);

       $request->session()->regenerateToken();
       return redirect()->route('board');
   }

渡ってきたユーザ属性をもとに、テナントID、ユーザ名をセットで格納します。

画面表示の部分も、ユーザIDを表示するようにしてみます。

api/resources/views/messageBoard/index.blade.php を編集します。

32行目あたりの、この $message->user->name の部分を $message->user_id に変更します。

修正前:

                   <div class="mt-4">
                       <p>
                           {{ $message->user->name }}
                           <span class="text-xs text-gray-500">
                               {{ $message->created_at->format('Y/m/d H:i') }}
                           </span>
                       </p>

修正後:

                   <div class="mt-4">
                       <p>
                           {{ $message->user_id }}
                           <span class="text-xs text-gray-500">
                               {{ $message->created_at->format('Y/m/d H:i') }}
                           </span>
                       </p>

これでマルチテナント対応ができました!

では、早速ログインして試してみましょう。

先ほどと同じように、SaaSus Platformで作成したログイン画面からログインを行います。

ログインすると、テナント名が先ほどSaaS 開発コンソールで設定したものに変わっているのが確認できます。

まだデータが無いので、いくつか投稿をしてみましょう。

ユーザ名も表示されていることが確認できました。

では、もう一度ログイン画面に戻り、 [email protected] でログインして、いくつか投稿してみましょう。

当然画面に反映されます。

では、もうひとつのテナントのユーザ、 [email protected] でログインしてみましょう

テナント名の表示が変わり、内容が空になっていることが確認できます。

自分のテナントの情報にしかアクセスできないことが確認できました。

では、同じようにいくつか投稿をした後に、 [email protected] でログインし、同一テナントの情報が表示できることを確認します。

このように、テナントごとの分離が完了しました。

今回の分離方式としては、プール型モデルで同一DB内での分離を行いシンプルな方式でテナント分離を行いました。スキーマ分離、データベース分離など、要件に応じてテナント分離の方式を選択する場合においても、同様にSaaSus SDKを利用してテナント情報を取得し実装することができます。

テナント分離ができたので、今度は料金関係の機能の実装をしてみましょう。

料金設定(プライシング)、利用量計測(メータリング)、請求(ビリング)の第一歩を実装してみます。請求においてはStripeという請求SaaSを利用します。Stripeを使わない場合は、請求の部分はスキップしてください。