Статьи

Включение авторизации в приложение Play 2.x (Scala)

Для людей, которые спешат, вот код и шаги .

В продолжении  Play 2.x (Scala) это соперник Spring MVC? — Введение , в этом блоге, я покажу, как использовать play20-auth, чтобы перейти на пример с кофе.

В качестве первого шага для этого примера нам нужно добавить зависимость в Build.scala, как показано ниже:

"jp.t2v" %% "play2.auth"      % "0.9",
"jp.t2v" %% "play2.auth.test" % "0.9" % "test",

Нам нужно определить объект домена Account.scala с разрешениями, как показано ниже,

object Account extends Table[Account]("ACCOUNT") {
  lazy val database = Database.forDataSource(DB.getDataSource())
 
  def id = column[Int]("ID")
  def email = column[String]("EMAIL")
  def password = column[String]("PASSWORD")
  def name = column[String]("NAME")
  def permission = column[Permission]("PERMISSION")
  // Every table needs a * projection with the same type as the table's type parameter
  def * = id ~ email ~ password ~ name ~ permission <> (Account.apply _, Account.unapply _)

Чтобы объект Account мог принять объект Permission.scala в качестве одного из столбцов, нам нужно реализовать MappedTypeMapper для разрешения, как показано ниже:

implicit val PermissionTimeMapper = MappedTypeMapper.base[Permission, String](
  d => Permission.stringValueOf(d),
  t => Permission.valueOf(t))
 
def valueOf(value: String): Permission = value match {
  case "Administrator" => Administrator
  case "NormalUser" => NormalUser
  case _ => throw new IllegalArgumentException()
}
 
def stringValueOf(value: Permission): String = value match {
  case Administrator => "Administrator"
  case NormalUser => "NormalUser"
  case _ => throw new IllegalArgumentException()
}

Следующим шагом является реализация свойства AuthConfigImpl, расширяющего AuthConfig в Application.scala, указывающего, что такое класс User для аутентификации, что представляет собой столбец, содержащий информацию о разрешениях, и страницу перенаправления, как показано ниже:

trait AuthConfigImpl extends AuthConfig {
 
  type Id = Int
 
  type User = Account
 
  type Authority = Permission
 
  val idTag = classTag[Id]
 
  val sessionTimeoutInSeconds = 3600
 
  def resolveUser(id: Id) = Account.findById(id)
 
  def loginSucceeded(request: RequestHeader) = Redirect(routes.CoffeesController.index)
 
  def logoutSucceeded(request: RequestHeader) = Redirect(routes.Application.login)
 
  def authenticationFailed(request: RequestHeader) = Redirect(routes.Application.login)
 
  def authorizationFailed(request: RequestHeader) = Forbidden("no permission")
 
  def authorize(user: User, authority: Authority) = (user.permission, authority) match {
    case (Administrator, _) => true
    case (NormalUser, NormalUser) => true
    case _ => false
  }
}

Теперь нам нужно отправить loginForm для аутентификации пользователя в Application.scala, как показано ниже,

val loginForm = Form {
  mapping("email" -> email, "password" -> text)(Account.authenticate)(_.map(u => (u.email, "")))
    .verifying("Invalid email or password", result => result.isDefined)
}

Метод аутентификации Account.scala выглядит следующим образом:

def authenticate(email: String, password: String): Option[Account] = {
  findByEmail(email).filter { account => password.equals(account.password) }
}

Для получения подробной информации об интеграции с действием в контроллере см. CoffeesController.scala, как показано ниже,

object CoffeesController extends Controller with AuthElement with AuthConfigImpl {
  def delete(pk: String) = StackAction(AuthorityKey -> Administrator) { implicit request =>
    database withSession {
      Home.flashing(Coffees.findByPK(pk).delete match {
        case 0 => "failure" -> "Entity has Not been deleted"
        case x => "success" -> s"Entity has been deleted (deleted $x row(s))"
      })
    }
  }
}

Обратите внимание, как мы используем StackAction и проверяем права администратора в контроллере.