0

我正在尝试为我的 API 实施身份验证。我创建了一个过滤器,它将根据令牌加载当前用户。

用户的加载工作(从示例中删除),但现在我想将用户添加到请求属性中,以便我可以在操作或控制器中访问它。但我无法让它工作:

package filters

import java.util.UUID
import javax.inject.Inject

import akka.stream.Materializer
import play.api.libs.typedmap.TypedKey
import play.api.mvc.{Filter, RequestHeader, Result}
import service.UserService

import scala.concurrent.{ExecutionContext, Future}

class LoadUserFilter @Inject() (implicit val mat: Materializer, ec: ExecutionContext) extends Filter {

  override def apply(nextFilter: RequestHeader => Future[Result]) (requestHeader: RequestHeader): Future[Result] = {
    val newRequest = requestHeader.addAttr(TypedKey.apply[String]("user"), "test")
    System.out.println(newRequest.attrs(TypedKey.apply[String]("user"))) // attribute not found....
    nextFilter(newRequest)
  }
}

这是我得到的例外:

Caused by: java.util.NoSuchElementException: key not found: user

出于某种原因,未添加该属性...我正在使用更新的请求,但它仍然无法正常工作。

我怎样才能解决这个问题?

4

2 回答 2

1

如果我正确理解了这个问题,您想从请求标头中提取用户 ID。我不确定过滤器是否是实现此目的的好地方。这可以通过不同的方式完成,我为此选择了 ActionBuilder 和 ActionTransformers。请参阅Play Framework 网站上的动作组合。

首先,您需要一个 RedirectingAction。然后我通过 TokenExtractAction 提取令牌,通过 UserIdExtractAction 提取用户 ID,并在 PermissionCheckAction 中检查 auth。

通过这种方式,它是可组合的、高性能的和可重用的。在您的控制器中,使用 SecureAction 而不是 Action。然后你就可以免费获得所有的奖励 userId、token 等。如果身份验证失败,它会默默地失败,不会弄乱你的控制器。这是基于我记得在 Play 网站上看到的示例。但我不得不延长它。在那提供的替代方案中,我发现这个对于验证每个 REST 请求情况最有用。

另外我正在使用actorsystem进行身份验证。您可以轻松地为自己构建一个。

case class RedirectingAction @Inject()(parser: BodyParsers.Default)(implicit val executionContext: ExecutionContext)
  extends ActionBuilder[Request, AnyContent] {

  def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]): Future[Result] = {
    block(request)
  }
}

case class TokenExtractAction @Inject()(parser: BodyParsers.Default)(
  implicit val executionContext: ExecutionContext)
  extends ActionTransformer[Request, TokenExtractRequest] {

  def transform[A](request: Request[A]): Future[TokenExtractRequest[A]] = Future.successful {
    val token = request.headers.get(Conf.REST_AUTH_HEADER)
    new TokenExtractRequest(token, Some(TokenTool.decodeToken(token.getOrElse("invalid_token")).plaintext), request)
  }
}

case class UserIdExtractAction @Inject()(parser: BodyParsers.Default)(implicit val executionContext: ExecutionContext)
  extends ActionTransformer[TokenExtractRequest, UserRequest] {

  def transform[A](request: TokenExtractRequest[A]): Future[UserRequest[A]] = Future.successful {
    new UserRequest(
      userAuthRole = TokenTool.tokenGetRoleFromToken(request.token),
      userId = TokenTool.tokenGetIdFromToken(request.token),
      token = request.token,
      plainTextToken = request.plainTextToken,
      request = request
    )
  }
}

case class PermissionCheckActionIndividual @Inject()(authSystem: AuthSystem)(implicit val executionContext: ExecutionContext)
    extends ActionFilter[UserRequest] {
  implicit val timeout: akka.util.Timeout = 5.seconds

  def filter[A](input: UserRequest[A]): Future[Option[Result]] = {
    val token                = input.headers.get(Conf.REST_AUTH_HEADER).orElse(Some("invalid token"))
    val authorizer: ActorRef = authSystem.authorizer
    // verify sig and check user validity
    val res = authorizer ? CheckUserValidityFromToken(token)

    res.flatMap {
      case true =>
        Future(None)
      case false =>
        Future(Some(Forbidden("Not permitted")))
    }
  }
}


class SecureActionProvider @Inject()(val authSystem: AuthSystem, val authService: AuthService)(implicit ec: ExecutionContext,
  parser: BodyParsers.Default) {

  def SecureAction: ActionBuilder[UserRequest, AnyContent] =
    RedirectingAction(parser) andThen TokenExtractAction(parser) andThen UserIdExtractAction(parser) andThen PermissionCheckActionIndividual(
      authSystem)
}

在您的控制器中:

def someSecureMethod = secureActionProvider.SecureAction.async ... {
   ...
}  

希望这可以帮助。

于 2018-02-11T17:35:48.620 回答
0

做这样的事情怎么样:

nextFilter(requestHeader).map { result =>
  result.withHeaders("user" -> "someUser")
}
于 2018-02-11T16:06:35.883 回答