Я пытаюсь добавить аутентификацию в одно приложение для платформы Play Framework.Play Framework Аутентификация в одностраничном приложении
То, что я хотел бы иметь что-то вроде:
def unsecured = Action {
Ok("This action is not secured")
}
def secured = AuthorizedAction {
// get the authenticated user's ID somehow
Ok("This action is secured")
}
Для традиционного веб-приложение, я уже сделал это, следуя Play Framework документы:
def authenticate = Action { implicit request =>
loginForm.bindFromRequest.fold(
formWithErrors => BadRequest(views.html.login(formWithErrors)),
user => {
Redirect(routes.Application.home).withSession(Security.username -> user._1)
}
)
}
def logout = Action {
Redirect(routes.Auth.login).withNewSession.flashing(
"success" -> "You are now logged out."
)
}
и Авторизованный Действие расширяя ActionBuilder следующим образом:
object AuthorizedAction extends ActionBuilder[Request] with Results {
/**
* on auth success: proceed with the request
* on auth failure: redirect to login page with flash
*/
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
// TODO: is "isDefined" enough to determine that user is logged in?
if(request.session.get("username").isDefined) {
block(request)
}
else {
Future.successful(Redirect(routes.Auth.login).flashing(
"failure" -> "You must be logged in to access this page."
))
}
}
}
Для одностраничных приложений h Тем не менее, этот подход больше не работает.
В этой статье Джеймс Уорд объясняет, как новый подход должен быть разработан и включает в себя реализацию Java: Securing SPA and rest services
Реализация была переделана в Scala Мариуса Soutier: Securing SPA in Scala
В своем примере, он реализует признак безопасности:
trait Security { self: Controller =>
val cache: CacheApi
val AuthTokenHeader = "X-XSRF-TOKEN"
val AuthTokenCookieKey = "XSRF-TOKEN"
val AuthTokenUrlKey = "auth"
/** Checks that a token is either in the header or in the query string */
def HasToken[A](p: BodyParser[A] = parse.anyContent)(f: String => Long => Request[A] => Result): Action[A] =
Action(p) { implicit request =>
val maybeToken = request.headers.get(AuthTokenHeader).orElse(request.getQueryString(AuthTokenUrlKey))
maybeToken flatMap { token =>
cache.get[Long](token) map { userid =>
f(token)(userid)(request)
}
} getOrElse Unauthorized(Json.obj("err" -> "No Token"))
}
}
Функции теперь обеспеченное как это вместо простого действия:
def ping() = HasToken() { token => userId => implicit request =>
user.findByID (userId) map { user =>
Ok(Json.obj("userId" -> userId)).withToken(token -> userId)
} getOrElse NotFound (Json.obj("err" -> "User Not Found"))
}
где .withToken определяется как:
implicit class ResultWithToken(result: Result) {
def withToken(token: (String, Long)): Result = {
cache.set(token._1, token._2, CacheExpiration)
result.withCookies(Cookie(AuthTokenCookieKey, token._1, None, httpOnly = false))
}
def discardingToken(token: String): Result = {
cache.remove(token)
result.discardingCookies(DiscardingCookie(name = AuthTokenCookieKey))
}
}
Я не нравится, как комплекс функция «пинг» выше стал, и предпочел бы использовать построитель Action (как в первом примере) , где аут-аут обнаруживается и обрабатывается в одной точке. (на данный момент, если я хочу защитить функции ping2 и ping3, каждый из них должен проверить, найден ли пользователь и что он имеет дело с «не найденным» случаем)
Я попытался собрать вдохновителя, вдохновленного по реализации Marius, в частности его использование cacheApi, которое необходимо.
Однако AuthorizedAction является объектом, а cacheApi необходимо вводить (поэтому необходимо изменить объект на одноэлементный класс) или не может быть объявлен в объекте без определения.
Я также чувствую, что AuthorizedAction должен оставаться объектом, для того, чтобы использоваться в качестве:
def secured = AuthorizedAction {
ли кто-нибудь, пожалуйста, прояснить путаницу, и, возможно, помочь с некоторыми деталями реализации?
спасибо
благодарит за ответ vdebergue. В моем случае метод «findByToken» находится в однопользовательском классе UserService, который необходимо ввести в «AuthorizedAction». Однако, если я это сделаю, Play рассчитывает указать userService в вызове authorizedAction: val authorizedAction = new AuthorizedAction (/ * указать службу пользователя здесь * /). Любая идея, как инъекция зависимости будет работать с вашим подходом? Также есть какая-либо причина для круглых скобок «()« следующий »class AuthorizedAction()»? – Khorkhe
Я обновил свой пример, введя userService в 'AuthorizedAction'. Я не использую 'Guice', но если вы это сделаете, было бы легко адаптировать код для его использования. Скобки после 'AuthorizedAction' не нужны в первом примере. – vdebergue
принято, хотя модифицировано для моего конкретного случая/нравится. большое спасибо! – Khorkhe