Play2で認証機能ありのSPAを扱う


## 概要

Webアプリを作るとき、最近は画面の構成はSPAでやるのが主流です。

SPAでのコンテンツの配置はこちらの記事で書きました。
[SPAの静的ファイルのデプロイの仕方](http://qiita.com/uryyyyyyy/items/a4b97709a019af1242be)

SPAの構成のログイン制御の仕方はこちらの記事でざっくり書きました。
[SPAでのログイン処理のやりかた](http://qiita.com/uryyyyyyy/items/56f9a80a829161ae1f8e)

play2でSPAのコンテンツを配信するやり方はこちらに書きました。
[Play2でのSPAコンテンツの配信の仕方](http://qiita.com/uryyyyyyy/items/0719dc4b25512b3d3e5f)

Play2で認証の処理をするやり方をこちらに書きました。
[Play2-authを使ってみる - 認可/Authorization編](http://qiita.com/uryyyyyyy/items/9216ebead22495670a92)

さて、それらを組み合わせて、play2で認証ありのSPAをどう扱うかについてまとめていきます。

## 環境

- Play 2.5
- React.js 15.x
- react-router（フロント側でのルーティング機構）

ソースコードはこちら。

https://github.com/uryyyyyyy/play2sample/tree/play2-spa-auth

## ゴール

- 認証はサーバー側でAPIを介して行う
- ルーティングは全てjs側で行う
- APIアクセス時に、ログイン・権限の有無を確認できる
  - ログインされてなければログイン画面にリダイレクトさせる
  - 権限がなければ、それっぽいアラートを出す。
- ログイン画面読み込み時に、既にログイン済みならホーム画面へリダイレクトさせる
- 他の画面読み込み時に、未ログインならログイン画面へリダイレクトさせる

## 手順

- サーバーサイドのルーティングを用意する
- フロントのコンテンツを配置する
- 動作確認する

とりあえず動かして様子を見たい方は、「動作確認する」から読んで頂ければと思います。

### サーバーサイドのルーティングを用意する

```text:routes
->  /api api.Routes

# static file
GET     /public/*file               controllers.Assets.versioned(path="/public", file: Asset)

# login/logout
GET     /authentication/login/:userId/:password               controllers.auth.AuthenticateController.login(userId: String, password: String)
GET     /authentication/logout              controllers.auth.AuthenticateController.logout

# html
GET /login    controllers.HomeController.login
GET /         controllers.HomeController.index
GET /*path    controllers.HomeController.indexAll(path)
```

```text:api.routes
GET     /authorize/admin              controllers.auth.AuthorizeController.checkAdminRole
GET     /authorize/normal              controllers.auth.AuthorizeController.checkNormalRole
```

ログイン処理は、面倒なのでgetでできるようにしています。本番では真似しないよう。。。

説明は省略しますが、ログイン時の流れとしては、

1. `/login`でログイン画面を表示する。
1. `/public/*file`でjsなどを持ってくる
1. `/authentication/login/:userId/:password`でログインする
2. `/api//authorize/admin` などへアクセスしてみて、権限管理を確かめる

という流れになります。

### フロントのコンテンツを配置する

最終的なコードはこちらを見てください。
https://github.com/uryyyyyyy/play2sample/tree/play2-spa-auth/webConsole/front

再掲となりますが、こちらにまとめてあります。

[SPAの静的ファイルのデプロイの仕方](http://qiita.com/uryyyyyyy/items/a4b97709a019af1242be)

[Play2でのSPAコンテンツの配信の仕方](http://qiita.com/uryyyyyyy/items/0719dc4b25512b3d3e5f)

### 動作確認する

コードを用意。
https://github.com/uryyyyyyy/play2sample/tree/play2-spa-auth

また、sbtとnodeが必要です。

```
# SPAのルートディレクトリへ移動
cd webConsole/front

# フロントのビルド
npm install
npm run build:prod

# webConsoleのルートディレクトリへ移動
cd ../

# フロントのコードをplayのpublicに配置
sh deployFront.sh

# プロジェクトルートに移動
cd ../

# playパッケージング
sbt webConsole/dist

# パッケージされたものを実行
cd webConsole/target/universal/
unzip play2sample-main-1.0.zip
cd play2sample-main-1.0/
chmod 700 ./bin/play2sample-main
./bin/play2sample-main
```

これで、`http://localhost:9000`につなげるようになります。
（以降の動作は、js側でalertを出して確認する形にしています。アラートが出ないなどの場合はブラウザ設定を確認してください。）

#### ログインしてなければログイン画面に戻ることを確認

`http://localhost:9000`にアクセスする。
すると、ホーム画面はログイン済みでないと開けない設定なので、「認証に失敗しました」のアラートの後でログイン画面にリダイレクトする。

#### ログインしたあとはログイン画面にリダイレクトされないことを確認

`http://localhost:9000/login`で、「login Normal」でログインする。
すると`http://localhost:9000`に飛びます。
以降、セッションが有効な期間（デフォルトでは1時間）の間はリロードしてもホーム画面のままです。
違うパス(`http://localhost:9000/sample`)に繋いでみてもちゃんとindex.htmlが読まれて、react-routerによってコンテンツが出てきていることが確認できます。

また、`http://localhost:9000/login`に繋ぎに行くと、「認証済みです」のアラートの後でホームに飛ばされます。

#### 権限制御できていることを確認（normal権限）

「normal API」を叩くと、「success」が返ってくることを確認します。
また、「admin API」を叩くと、許可されてない動作なので403が返り、ホーム画面にリダイレクトされます。

#### ログアウトするとログイン画面しか開けなくなることを確認

「logout」ボタンを押すと、セッショントークン（cookie）が破棄されます。
すると、初回時と同様にホーム画面にはアクセスできなくなります。

#### 権限制御できていることを確認（admin権限）

`http://localhost:9000/login`で、「login Admin」でログインする。

「normal API」も「admin API」も「success」が返ってくることを確認します。
先ほどのnormalログインとは違う挙動になったことがわかると思います。

## まとめ

ここにたどり着くまで、たくさんの記事を書いてきました。
何かご質問や不明点があればお気軽にコメントください。