はじめに
この記事では
Play Frameworkにおいて、Action Refiner
を利用した認証機能の実装方法についてお伝えします。
大まかな手順
-
Action Refiner
を継承したアクションオブジェクトを作成する -
アクションオブジェクト内に
refine
メソッドを定義する
2-1. リクエストの属性に値を格納する -
Action
とアクションオブジェクトをチェーンで繋ぎ、コントローラーで使用する
Action Refiner とは
- リクエストをすぐにインターセプトして結果(
Result
型の値)を返す - 新しいリクエスト(
Request
型の値)で処理を続行する
上記のどちらの処理を行うかを判別して、実行してくれます。
この特性を利用して、認証機能を実装することが出来ます。
実装手順
今回の実装例としては、
ユーザーのログイン認証機能について実装していきます。
1.Action Refiner
を継承したアクションオブジェクトを作成する
extends
で継承することができます。
▼実装コード
object UserAction extends ActionRefiner[Request, Request] {}
2.アクションオブジェクト内にrefine
メソッドを定義する
Action Refiner
トレイトに定義されているrefine
メソッドを利用します。
refine
メソッドは抽象メソッドの為、自分で定義して利用することで、Action Refiner
の恩恵を受けることができます。
※ 戻り値がFuture[Either[Result, P[A]]]
になるように定義する。
▼実装コード
object UserAction extends ActionRefiner[Request, Request] {
def refine[A](request: Request[A]): Future[Either[Result, P[A]]] = Future.successful {
// ログインしている => Right(newRequest)
// ログインしていない => Left(Results.Redirect("/login_form"))
request.session.get("userId")
.map(userId => {
for {
// userを取得する
user <- UserRepository.findById(userId)
} yield {
// 取得したuserをリクエストの属性に格納する(後述で説明します)
val newRequest: Request[A] = request.addAttr(UserAttr.User, user)
Right(newRequest)
}
})
.getOrElse(Future.successful(Left(Results.Redirect("/login_form"))))
}
}
2-1.リクエストの属性に値を格納する
参考:https://www.playframework.com/documentation/2.8.x/Highlights26#Request-attributes
リクエストには値を書き込むことができます。
以下の手順で実装します。
2-1-1.属性KEYを定義・作成する
// リクエストの属性を格納する場所(キー)を作る
object UserAttrs {
val User: TypedKey[User] = TypedKey.apply[User]("user")
}
TypedKey.apply[格納する型]
=> TypedKey
を作成する事ができる。
※ TypedKey
の名前は引数に渡した文字列となる。(上のコードで言うと"user"
)
2-1-2.リクエストに属性KEYと値のセットを書き込む(リクエストに属性を付与する)
// リクエストのTypedMapに属性KEYと値のセットを格納する
// 取得したuserをリクエストの属性に格納する
val newRequest: Request[A] = request.addAttr(UserAttr.User, user)
TypedMap
=> 「TypedKey」と「値」を格納する不変のマップ。
例)
// 型
TypedMap(TypedKey[String] -> String)
// キーのタイプ([]の部分)は、属性のタイプを示す
キーの型: TypedKey[String]
値の型: String
2-1-3.コントローラーでリクエストの属性値を取得する
// 属性値を取得する
newRequest.attrs(UserAttrs.User)
2-1-2. で書き込んだ値を取得する方法です。(後述のコントローラー側で使用します)
3.Action
とアクションオブジェクトをチェーンで繋ぎ、コントローラーで使用する
andThen
を使ってアクション関数をつなぎ、1つのアクションを作成することができます。
▼実装コード
def view = (Action andThen UserAction) { request =>
val user: User = request.attrs(UserAttrs.User)
Ok("User " + user.name)
}
『注意するポイント』
先頭のみAction Builder
で始まるアクションにしましょう。
※ 上記の実装コードで言うと、Action
を指します。
Action Builder
には
- 関数合成のためのcompose処理 (これは他の種別にもある)
- 非同期処理に必要なコンテクスト管理
- リクエストをパースする処理
などが含まれており、
チェーンする後続定義として、
- 非同期処理に必要なコンテクスト管理
- リクエストをパースする処理
の定義機能を含むのは冗長であるからです。
そのような理由から、
Action Builder
定義は、基本的には最初の1個目であり、
andThen
で後に使うようには設計されていません。
まとめ
Action Refiner
の使い方について紹介しました。
このように、Action Refiner
を使用することで、認証機能を実装することができます。
Action Refiner
以外にも、
というトレイトもありますので、ぜひ調べて使用してみて下さい。