Security traitはScala版Playに標準で入っているものです。詳細は以下のURLを参照ください。
また、紹介するサンプルコードは以下においています。
Play標準APIでの認証・認可は非常にプリミティブなものしか提供されていないません。OAuthやOpenIDなどに対応する必要が出た場合は、全て自力で実装するのか、既存のライブラリを使うのか検討してみる価値はあると思います。
How to implement security Authorization using scala and play?
認証:ID・パスワードの照合
このサンプルではメールアドレスとパスワードでユーザを認証します。loginFormの定義部分で、「UserDAO.validate(User(email, password))」としている箇所で送られてきたメールアドレスとパスワードを持つユーザがDBに登録されているか、確認します。
class Application @Inject()(val messagesApi: MessagesApi) extends Controller with Secured with I18nSupport {
val loginForm = Form(
tuple(
"email" -> nonEmptyText,
"password" -> nonEmptyText
) verifying ("Invalid email or password", result => result match {
case (email, password) => UserDAO.validate(User(email, password))
})
)
(省略)
メールアドレスとパスワードが正しいものであると確認が取れれば、メールアドレスをセッションに登録します。セッションにメールアドレスが存在することが認証済みの合図とします。ここで、Security.usernameは標準APIです。ランダムなセッション変数名を自動的に生成してくれるため、セッション名を推測できなくする上で役立つと思います。application.confに「session.username」が定義されていれば、セッション名の一部として使われます。
def authenticate = Action { implicit request =>
request.session.get(Security.username).map { user =>
Redirect(routes.Application.index).withNewSession.flashing()
}.getOrElse {
loginForm.bindFromRequest.fold(
formWithErrors => BadRequest(views.html.login(formWithErrors)),
user => Redirect(routes.Application.index).withSession(Security.username -> user._1)
)
}
}
(省略)
認可
特定のURLは認証済みの場合にのみアクセスを許可したいとします。そういうケースについては、Action Composition(アクションの合成)という機能を使い、既存のアクションが呼び出される直前に認証関係のアクションを実行することで保護します。以下のwithAuthは認証済みかどうかを確認するアクションです。
def index = withAuth { user => implicit request =>
Ok(views.html.index(user.email))
}
withAuthは認証・認可関係をまとめたtraitに定義しています。
withUserはセッション変数を元にユーザの情報をDBから取ってきて、アクション内で使う場合のメソッドです。withUserもwithAuthを呼んでいます。
trait Secured {
private def username(request: RequestHeader) = request.session.get(Security.username)
private def onUnauthorized(request: RequestHeader) = Results.Redirect(routes.Application.login)
def withAuth(f: => String => Request[AnyContent] => Result) = {
Security.Authenticated(username, onUnauthorized) { user =>
Action(request => f(user)(request))
}
}
def withUser(f: User => Request[AnyContent] => Result) = withAuth { username => implicit request =>
UserDAO.findOneByUsername(username).map { user =>
f(user)(request)
}.getOrElse(onUnauthorized(request))
}
}
以下のように、アクションにwithUserをかぶせると、内部ではwithAuth→withUser→index2という順番に実行されます。認証できたユーザの情報を最終的に実行したいアクションに渡せます。
def index2 = withUser { user => implicit request =>
val email = user.email
Ok(views.html.index2(email))
}