はじめに
この記事では
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以外にも、
というトレイトもありますので、ぜひ調べて使用してみて下さい。