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

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

最佳答案

是的,Silhouette允许您实现多个authenticators。这是您实现 JWTAuthenticator 的方法,该jQueryt提供其JWT身份验证器服务以及 CookieAuthenticator :

  • 正如注释中已经指出的Douglas Liu一样,您将需要创建其他 environment 类型。它应该将Identity与相应的Authenticator连接起来。

  • 例如:
    trait CookieEnv extends Env {
      type I = Account
      type A = CookieAuthenticator
    }
    
    trait JWTEnv extends Env {
      type I = Account
      type A = JWTAuthenticator
    }
    
  • 在Silhouette模块中实现JWT绑定(bind)。请查看 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)
      }
    
    // ...
    
    }
    
  • JWTAuthenticator configuration settings添加到您的silhouette.conf中:

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

  • 例如,在您的app.routes文件中,添加以下行:
    # JWT Authentication
    POST        /api/jwt/authenticate        controllers.auth.api.AuthController.authenticate
    
  • 最后,在您的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"))))
      }
    }
    

    10-06 11:15