コントローラにミックスインして、 AdminAction
を使って処理を書くことで、認証されているときにのみ画面を表示することができます。
ユーザー名とパスワードは Basic 認証で行われ、 AdminAction
から与えられた request.userName
で、認証された名前が得られます。
AdminSecure
という名前ですが、管理者に限っているということではなく、Basic 認証を行っているだけです。
コントローラごとに認証される名前とパスワードを変えたいときは checkAccount
をオーバーライドしてください。
AdminAction.scala
package controllers
import play.api._
import play.api.mvc._
/**
* 管理者のアカウントを取り扱うモジュール
*/
trait AdminSecure {
import play.api.mvc.Results._
private[this] val accounts = Map("admin" -> "password")
protected val basicRealm = "(my realm)"
/** ユーザー名とパスワードが一致するかを返します */
protected def checkAccount(userName: String, password: String) =
accounts.get(userName).filter(_ == password).nonEmpty
/**
* 認証を確認して実行するアクション
*/
object AdminAction {
private val AUTHORIZATION = "authorization"
private val WWW_AUTHENTICATE = "WWW-Authenticate"
private def realm = "Basic realm=\"%s\"".format(basicRealm)
def apply(f: AuthenticatedRequest[AnyContent] => Result): Action[AnyContent] =
apply(BodyParsers.parse.anyContent)(f)
def apply[A](p: BodyParser[A])(f: AuthenticatedRequest[A] => Result) = Action(p) { request =>
request.headers.get(AUTHORIZATION) match {
case Some(BasicAuthorization(name, pw)) if checkAccount(name, pw) =>
f(new AuthenticatedRequest(name, request))
case _ => Unauthorized("need admin login").withHeaders(WWW_AUTHENTICATE -> realm)
}
}
/**
* Basic 認証の値の取り扱い
*/
private object BasicAuthorization {
private val basicPefix = "Basic "
private val AUTHORIZATION_PARAMS = """([^:]+?):(.+)""".r
/**
* Base64 エンコードで文字列化された Basic 認証値を、ユーザー名とパスワードに分解して返します
*
* @param auth Base64 エンコード文字列
* @return 名前とパスワード
*/
private[this] def decode(auth: String): Option[(String, String)] = {
val d = javax.xml.bind.DatatypeConverter.parseBase64Binary(auth)
new String(d, "utf-8") match {
case AUTHORIZATION_PARAMS(username, password) => Some(username, password)
case _ => None
}
}
/**
* WWW-Authenticate ヘッダの値から、ユーザー名とパスワードを取り出します。
*
* @param authorization WWW-Authenticate ヘッダ値。ヘッダの名前は含まない。
* @return 名前とパスワード
*/
def unapply(authorization: String): Option[(String, String)] = authorization match {
case s if s startsWith basicPefix => decode(s drop basicPefix.length)
case _ => None
}
}
/** 認証されて実行するリクエスト */
class AuthenticatedRequest[A] private[controllers]
(val userName: String, request: Request[A]) extends WrappedRequest(request)
}
}
実装方法
Application.scala
package controllers
import play.api._
import play.api.mvc._
object Application extends Controller with AdminSecure {
def adminHome = AdminAction { implicit request =>
Ok("Welcome back, " + request.userName)
}
}