使用 Play Framework 和 Scala 管理用户身份验证(3)Silhouette 简介
- UID
- 1066743
|
使用 Play Framework 和 Scala 管理用户身份验证(3)Silhouette 简介
Silhouette 简介 Silhouette 框架的首要特征是灵活性。Silhouette 实现了一组独立的身份验证组件,开发人员需要配置和组合它们来构建身份验证逻辑。主要组件包括:
- 身份服务:Silhouette 依靠 IdentityService 特征的一个实现来处理所有与检索用户相关的操作。通过这种方式,用户管理完全与框架分离开来。UserService 类(已在 “ ” 一节中介绍)实现一个由 MongoDB 支持的身份服务。
- AuthInfoRepository:Silhouette 需要知道如何持久化用户凭据。该框架将此工作委派给 AuthInfoRepository 特征的一个实现。该应用程序使用一个综合存储库,其中组合了 PasswordInfoDao 和 OAuth1InfoDao 类,这两个类都已在 “ ” 一节中介绍。
- 身份验证器:身份验证器在成功完成身份验证后跟踪用户。它们是存储数据的令牌,比如其有效性状态和用户的登录信息。Silhouette 拥有基于 cookie、Play 无状态会话、HTTP 标头和 JSON Web Tokens (JWT) 的实现。
- 身份验证器服务:每个身份验证器都有一个关联的身份验证器服务,负责身份验证器的生命周期:创建、初始化、更新、续约和过期。
- 环境:环境定义一个 Silhouette 应用程序需要的关键组件。它按用户和身份验证器类型(在该应用程序中,为 中定义的 User 类和一个 CookieAuthenticator)来进行类型参数化。环境是通过传递身份服务实现 (UserService) 和身份验证器服务实现来构建的。我使用了 CookieAuthenticatorService 类,这是 CookieAuthenticator 类型所需要的。
- 提供程序:提供程序是一个处理用户的身份验证的服务。该应用程序使用 Silhouette 的 CredentialsProvider 来执行本地身份验证,还使用了 OAuth1 TwitterProvider。
- SocialProviderRegistry:这是该应用程序支持的所有社交服务提供程序的占位符。在本例中,它包含 TwitterProvider 实例。
配置 Silhouette 组件注意您的 Play 版本 从 2.4.0 版开始,Play Framework 框架采用了依赖注入作为一种内置机制。旧版本采用了完全不同的机制来将参数传递给控制器。一些库仍无法用于 Play 2.4.x。由于 Play 相对未成熟,主要版本之间的变化可能非常大。
Silhouette 组件通过依赖注入的方式来配置和组合。Play 使用 作为默认的依赖注入实现。(如果您愿意,可以插入其他实现。)Guice module.Module 类定义了 Silhouette 需要的绑定,首先是基本声明,如 清单 12 所示。
清单 12. 依赖注入绑定1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| class Module extends AbstractModule with ScalaModule {
def configure() {
bind[IdentityService[User]].to[UserService]
bind[UserDao].to[MongoUserDao]
bind[UserTokenDao].to[MongoUserTokenDao]
bind[DelegableAuthInfoDAO[PasswordInfo]].to[PasswordInfoDao]
bind[DelegableAuthInfoDAO[OAuth1Info]].to[OAuth1InfoDao]
bind[IDGenerator].toInstance(new SecureRandomIDGenerator())
bind[PasswordHasher].toInstance(new BCryptPasswordHasher)
bind[FingerprintGenerator].toInstance(new DefaultFingerprintGenerator(false))
bind[EventBus].toInstance(EventBus())
bind[Clock].toInstance(Clock())
}
// ... Bindings for Silhouette components follow
}
|
清单 12 代码定义了 Silhouette 的身份服务;用户、密码和 OAuth1 DAO;以及 Silhouette 的主要组件需要的一些对象的绑定。 清单 13 显示了这些组件的定义,也包含在 module.Module 中。
清单 13. 依赖注入绑定(续)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| @Provides def provideEnvironment(
identityService: IdentityService[User],
authenticatorService: AuthenticatorService[CookieAuthenticator],
eventBus: EventBus): Environment[User, CookieAuthenticator] = {
Environment[User, CookieAuthenticator](identityService, authenticatorService, Seq(), eventBus)
}
@Provides def provideAuthenticatorService(
fingerprintGenerator: FingerprintGenerator,
idGenerator: IDGenerator,
configuration: Configuration,
clock: Clock): AuthenticatorService[CookieAuthenticator] = {
val config = configuration.underlying.as[CookieAuthenticatorSettings]("silhouette.authenticator")
new CookieAuthenticatorService(config, None, fingerprintGenerator, idGenerator, clock)
}
@Provides def provideCredentialsProvider(
authInfoRepository: AuthInfoRepository,
passwordHasher: PasswordHasher): CredentialsProvider = {
new CredentialsProvider(authInfoRepository, passwordHasher, Seq(passwordHasher))
}
@Provides def provideAuthInfoRepository(
passwordInfoDAO: DelegableAuthInfoDAO[PasswordInfo],
oauth1InfoDAO: DelegableAuthInfoDAO[OAuth1Info]): AuthInfoRepository = {
new DelegableAuthInfoRepository(passwordInfoDAO, oauth1InfoDAO)
}
@Provides def provideTwitterProvider(
httpLayer: HTTPLayer,
tokenSecretProvider: OAuth1TokenSecretProvider,
configuration: Configuration): TwitterProvider = {
val settings = configuration.underlying.as[OAuth1Settings]("silhouette.twitter")
new TwitterProvider(httpLayer, new PlayOAuth1Service(settings), tokenSecretProvider, settings)
}
@Provides def provideOAuth1TokenSecretProvider(
configuration: Configuration, clock: Clock): OAuth1TokenSecretProvider = {
val cfg = configuration.underlying.as[CookieSecretSettings]("silhouette.oauth1TokenSecretProvider")
new CookieSecretProvider(cfg, clock)
}
@Provides def provideSocialProviderRegistry(
twitterProvider: TwitterProvider): SocialProviderRegistry = {
SocialProviderRegistry(Seq(twitterProvider))
}
|
Environment(它通过一个 CookieAuthenticator 来验证 User 实例)通过以下实体来实例化:
- 一个绑定到 UserService 类的 IdentityService
- 一个绑定到 CookieAuthenticatorService 的 AuthenticatorService
- 一个空的请求提供程序列表(本应用程序中未使用)
- 一个 EventBus,可用于广播身份验证事件(本应用程序中未使用)
CredentialsProvider 通过注入一个由 DelegableAuthInfoRepository 实现支持的 AuthInfoRepository 来创建,而该实现将凭据持久化任务委派给 PasswordInfoDao 和 Oauth1InfoDao 类。TwitterProvider 类需要 OAuth1TokenSecretProvider 特征的实现,该实现定义如何在 OAuth1 运行期间持久化令牌机密。最后,应用程序定义了一个 SocialProviderRegistry,其中列出了 TwitterProvider 作为唯一可用的社交服务提供程序。
Silhouette 配置文件 cookie 身份验证器服务、Twitter 提供程序和 OAuth1 令牌机密提供程序的绑定访问在 conf/silhouette.conf 文件中定义的配置属性(如 清单 14 所示),该文件包含在主要 conf/application.conf 文件中(参见 )。
清单 14. Silhouette 配置文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| authenticator.cookieName="authenticator"
authenticator.cookiePath="/"
authenticator.secureCookie=false
authenticator.httpOnlyCookie=true
authenticator.useFingerprinting=true
authenticator.authenticatorIdleTimeout=30 minutes
authenticator.authenticatorExpiry=12 hours
oauth1TokenSecretProvider.cookieName="OAuth1TokenSecret"
oauth1TokenSecretProvider.cookiePath="/"
oauth1TokenSecretProvider.secureCookie=false
oauth1TokenSecretProvider.httpOnlyCookie=true
oauth1TokenSecretProvider.expirationTime=5 minutes
twitter.requestTokenURL="https://api.twitter.com/oauth/request_token"
twitter.accessTokenURL="https://api.twitter.com/oauth/access_token"
twitter.authorizationURL="https://api.twitter.com/oauth/authorize"
twitter.callbackURL="http://dwdemo.com:9000/auth/social/twitter"
twitter.consumerKey=${?TWITTER_CONSUMER_KEY}
twitter.consumerSecret=${?TWITTER_CONSUMER_SECRET}
|
请参阅 查看这些属性的详细解释。可以注意到,其中使用了 TWITTER_CONSUMER_KEY 和 TWITTER_CONSUMER_SECRET 环境变量来避免在源代码中公开 OAuth1 机密。
安全操作 Silhouette 定义了两个对实现受保护的请求处理函数很有用的临时操作。这些操作可用于 Silhouette 控制器特征中包含的控制器组合:
- UserAwareAction:此操作可以由经过身份验证的用户执行。该操作收到的请求将具有一个 Option[U] 类型(对于一些依赖于应用程序的用户,类型为 U)的 identity 属性,如果请求是经过身份验证的用户发出的,将会定义该属性。
- SecuredAction:此操作必须由经过身份验证的用户执行。否则,它将调用应用程序的错误处理函数的 onNotAuthorized 方法(在我的应用程序中,它重定向到登录页面,如 “ ” 一节中所述)。此操作设置了一个类型为 U 的请求 identity 属性(对于一些依赖于应用程序的用户,类型为 U),如 清单 15 所示。清单 15. Silhouette 中受保护的请求
1
2
3
4
5
6
7
8
9
10
11
12
13
| class ApplicationController extends Silhouette {
def someUserAwareAction = UserAwareAction.async {implicit request =>
request.identity match {
case None => // Request sent anonymously ...
case Some(u) => // Request sent by authenticated user u
}
}
def someSecureAction = SecureAction.async { implicit request =>
logger.info(s"Logged user: ${request.identity}")
...
}
}
|
|
|
|
|
|
|