6

我使用play-silhouette-seed作为我的应用程序的模板。所以在我的项目中,我使用了基于 cookie 的身份验证器 ( CookieAuthenticator)。这绝对可以正常工作,即使对于通过嵌入在我的 Twirl 模板中的 JavaScript 进行的 REST 调用也是如此。但是,现在我还想在浏览器以外的客户端中以编程方式进行 REST 调用。因此,我必须Set-Cookie: authenticator=...在每个响应中检索元素并将其设置为我的请求的一部分。在我嵌入在我的 Twirl 模板中并在浏览器中呈现的 JavaScript 片段中,这没问题,因为我不必处理它,但对于其他客户端(服务器等),这会导致头痛。

JWTAuthenticator除了我的CookieAuthenticator. 这甚至受支持,还是我必须完全切换到JWTAuthenticator?此外,我是否需要单独的操作,即使除了身份验证器之外的所有内容都应该是相同的实现?

4

1 回答 1

3

是的,Silhouette 允许您实现多个身份验证器。以下是您如何实现JWTAuthenticator提供其 JWT 身份验证器服务的方法以及您的CookieAuthenticator

  1. 正如 Douglas Liu 已经在评论中指出的那样,您将需要创建一个额外的环境类型。它应该Identity与相应的Authenticator.

例如:

trait CookieEnv extends Env {
  type I = Account
  type A = CookieAuthenticator
}

trait JWTEnv extends Env {
  type I = Account
  type A = JWTAuthenticator
}
  1. 在您的 Silhouette 模块中实现 JWT 绑定。请查看play-silhouette-angular-seed以获取完整示例。

例如:

class SilhouetteModule extends AbstractModule with ScalaModule {

  def configure() {
    bind[Silhouette[CookieEnv]].to[SilhouetteProvider[CookieEnv]]
    bind[Silhouette[JWTEnv]].to[SilhouetteProvider[JWTEnv]]
    // ...
    ()
  }

   @Provides
  def provideCookieEnvironment(
                                userService: AccountService,
                                authenticatorService: AuthenticatorService[CookieAuthenticator],
                                eventBus: EventBus): Environment[CookieEnv] = {

    Environment[CookieEnv](
      userService,
      authenticatorService,
      Seq(),
      eventBus
    )
  }

  @Provides
  def provideJWTEnvironment(
                             userService: AccountService,
                             authenticatorService: AuthenticatorService[JWTAuthenticator],
                             eventBus: EventBus): Environment[JWTEnv] = {

    Environment[JWTEnv](
      userService,
      authenticatorService,
      Seq(),
      eventBus
    )
  }

// ...

  @Provides
  def provideCookieAuthenticatorService(
                                         @Named("authenticator-cookie-signer") cookieSigner: CookieSigner,
                                         @Named("authenticator-crypter") crypter: Crypter,
                                         fingerprintGenerator: FingerprintGenerator,
                                         idGenerator: IDGenerator,
                                         configuration: Configuration,
                                         clock: Clock): AuthenticatorService[CookieAuthenticator] = {

    val config = configuration.underlying.as[CookieAuthenticatorSettings]("silhouette.authenticator")
    val encoder = new CrypterAuthenticatorEncoder(crypter)

    new CookieAuthenticatorService(config, None, cookieSigner, encoder, fingerprintGenerator, idGenerator, clock)
  }

  @Provides
  def provideJWTAuthenticatorService(
                                      @Named("authenticator-crypter") crypter: Crypter,
                                      idGenerator: IDGenerator,
                                      configuration: Configuration,
                                      clock: Clock): AuthenticatorService[JWTAuthenticator] = {

    val config = configuration.underlying.as[JWTAuthenticatorSettings]("silhouette.authenticator")
    val encoder = new CrypterAuthenticatorEncoder(crypter)

    new JWTAuthenticatorService(config, None, encoder, idGenerator, clock)
  }

// ...

}
  1. JWTAuthenticator 配置设置添加到您的silhouette.conf

例如:

authenticator.fieldName = "X-Auth-Token"
authenticator.requestParts = ["headers"]
authenticator.issuerClaim = "Your fancy app"
authenticator.authenticatorExpiry = 12 hours
authenticator.sharedSecret = "!!!changeme!!!"
  1. 通过 JWT 创建单独的身份验证路由:

例如,在您的app.routes文件中,添加以下行:

# JWT Authentication
POST        /api/jwt/authenticate        controllers.auth.api.AuthController.authenticate
  1. 最后,在你的 中AuthController,添加相应的authenticate方法。

示例代码(改编自SignInController.scala):

implicit val dataReads = (
  (__ \ 'email).read[String] and
    (__ \ 'password).read[String] and
    (__ \ 'rememberMe).read[Boolean]
  ) (SignInForm.SignInData.apply _)

def authenticate = Action.async(parse.json) { implicit request =>
  request.body.validate[SignInForm.SignInData].map { signInData =>
    credentialsProvider.authenticate(Credentials(signInData.email, signInData.password)).flatMap { loginInfo =>
      accountService.retrieve(loginInfo).flatMap {
        case Some(user) => silhouette.env.authenticatorService.create(loginInfo).map {
          case authenticator if signInData.rememberMe =>
            val c = configuration.underlying
            authenticator.copy(
              expirationDateTime = clock.now + c.as[FiniteDuration]("silhouette.authenticator.rememberMe.authenticatorExpiry"),
              idleTimeout = c.getAs[FiniteDuration]("silhouette.authenticator.rememberMe.authenticatorIdleTimeout")
            )
          case authenticator => authenticator
        }.flatMap { authenticator =>
          Logger.info(s"User ${user._id} successfully authenticated.")
          silhouette.env.eventBus.publish(LoginEvent(user, request))
          silhouette.env.authenticatorService.init(authenticator).map { token =>
            Ok(Json.obj("token" -> token))
          }
        }
        case None => Future.failed(new IdentityNotFoundException("Couldn't find user."))
      }
    }.recover {
      /* Login did not succeed, because user provided invalid credentials. */
      case e: ProviderException =>
        Logger.info(s"Host ${request.remoteAddress} tried to login with invalid credentials (email: ${signInData.email}).")
        Unauthorized(Json.obj("error" -> Messages("error.invalidCredentials")))
    }
  }.recoverTotal {
    case e: JsError =>
      Logger.info(s"Host ${request.remoteAddress} sent invalid auth payload. Error: $e.")
      Future.successful(Unauthorized(Json.obj("error" -> Messages("error.invalidPayload"))))
  }
}
于 2017-03-11T08:43:37.587 回答