如何实现多个剪影身份验证器?
Posted
技术标签:
【中文标题】如何实现多个剪影身份验证器?【英文标题】:How to implement multiple Silhouette Authenticators? 【发布时间】:2017-07-01 18:29:26 【问题描述】:我使用play-silhouette-seed 作为我的应用程序的模板。所以在我的项目中,我使用基于 cookie 的身份验证器 (CookieAuthenticator
)。这绝对可以正常工作,即使对于通过嵌入在我的 Twirl 模板中的 javascript 进行的 REST 调用也是如此。但是,现在我还想在浏览器以外的客户端中以编程方式进行 REST 调用。因此,我必须在每个响应中检索 Set-Cookie: authenticator=...
元素并将其设置为我的请求的一部分。在我嵌入在我的 Twirl 模板中并在浏览器中呈现的 JavaScript sn-p 中,这没问题,因为我不必处理它,但对于其他客户端(服务器等),这会导致头痛。
除了我的CookieAuthenticator
之外,我现在还想实现一个JWTAuthenticator
。这甚至受支持,还是我必须完全切换到JWTAuthenticator
?此外,我是否需要单独的操作,即使除了身份验证器之外的所有内容都应该是相同的实现?
【问题讨论】:
我目前也在探索这种可能性。由于一个 Env 只能有一个 Authenticator,我相信您需要 2 Env 来托管 2 Authenticator。因此,您需要将控制器分开以使用不同的 Env。 【参考方案1】:是的,Silhouette 允许您实现多个authenticators。下面介绍了如何实现 JWTAuthenticator
,它提供 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 绑定。请查看 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"))))
【讨论】:
这是一个很好的开始,但我不明白为什么不使用 sillhouette 的动作,对我来说这似乎可以减轻大部分工作。 @Matthias -authenticator
中的 authenticator.fieldName = "X-Auth-Token"
是什么。是Silhouette
暴露的一些全局配置对象吗?我注意到在文档中使用了authenticator
,但我无法理解它的来源?它是用于所有类型的身份验证器的单个对象吗?
@ManuChadha 你如何命名并不重要。例如,CookieAuthenticatorService
的第一个参数需要一个 CookieAuthenticatorSettings
类型的实例,该实例是在位于 Silhouette 模块中的 provideCookieAuthenticatorService
方法中创建的(参见答案中提供的示例)。
@Matthias - 我不知道类型安全配置库。所以silhouette.conf
是我在其中定义各种属性的文件(例如authenticator.cookieName = "someName"
)。该文件与类型安全配置库兼容,并且该文件(和属性)可以由Configuration
对象(configuration.underlying.as[CookieAuthenticatorSettings]("silhouette.authenticator")
)读取。如果我有两个不同的身份验证器(比如凭据和 JWT),那么我需要指定它们的配置创建两个单独的组(例如autheticatorCredential.cookieName
和authenticatorJWT.fieldName
@ManuChadha 如果参数与各自的 class
定义匹配,那应该可以工作。但是,如果您愿意,也可以保留 authenticator
作为两个身份验证器的基础。以上是关于如何实现多个剪影身份验证器?的主要内容,如果未能解决你的问题,请参考以下文章
Node.js - JWT、API:如何实现多个 Passport.js 身份验证中间件?
如何在同一个网站的多个地方使用 laravel fortify 进行身份验证