Accessing resources protected by OAuth

OAuth 是发布受保护数据并与之交互的一种简单方式。它是授与你访问权的更安全可靠的方式。例如,你可以通过它访问你在 Twitter 上的用户数据。

OAuth 有 2 个非常不同的版本:OAuth 1.0OAuth 2.0。版本 2 非常简单,无需库和 helper 的支持。因此 Play 只提供 OAuth 1.0 的支持。

用法

想用 OAuth,首先需要把 ws 库添加到你的 build.sbt 文件中:

  1. libraryDependencies ++= Seq(
  2. ws
  3. )

需要的信息

OAuth 需要你将你的应用注册到服务提供方。确保检查了你提供的回调 URL,因为如果回调 URL 不匹配的话,服务提供方可能会拒绝你的调用。本地使用时,你可以在 /etc/hosts 中为本机伪造一个域名。

服务提供方会给你:

  • 应用 ID
  • 密钥
  • 请求令牌 URL(Request Token URL)
  • 访问令牌 URL(Access Token URL)
  • 授权 URL

验证流程

大部分事情都由 Play 的库完成。

  • 从服务器获取请求令牌(服务器之间的调用)。
  • 将用户重定向到服务提供方,在那里他会授权你的应用可以使用他的数据。
  • 服务提供方会将用户重定向回去,并给你一个检验器。
  • 有了检验器,你就可以将请求令牌换成访问令牌(服务器之间的调用)。

现在你可以用访问令牌去访问受保护的用户数据了。

例子

  1. object Twitter extends Controller {
  2. val KEY = ConsumerKey("xxxxx", "xxxxx")
  3. val TWITTER = OAuth(ServiceInfo(
  4. "https://api.twitter.com/oauth/request_token",
  5. "https://api.twitter.com/oauth/access_token",
  6. "https://api.twitter.com/oauth/authorize", KEY),
  7. true)
  8. def authenticate = Action { request =>
  9. request.getQueryString("oauth_verifier").map { verifier =>
  10. val tokenPair = sessionTokenPair(request).get
  11. // We got the verifier; now get the access token, store it and back to index
  12. TWITTER.retrieveAccessToken(tokenPair, verifier) match {
  13. case Right(t) => {
  14. // We received the authorized tokens in the OAuth object - store it before we proceed
  15. Redirect(routes.Application.index).withSession("token" -> t.token, "secret" -> t.secret)
  16. }
  17. case Left(e) => throw e
  18. }
  19. }.getOrElse(
  20. TWITTER.retrieveRequestToken("http://localhost:9000/auth") match {
  21. case Right(t) => {
  22. // We received the unauthorized tokens in the OAuth object - store it before we proceed
  23. Redirect(TWITTER.redirectUrl(t.token)).withSession("token" -> t.token, "secret" -> t.secret)
  24. }
  25. case Left(e) => throw e
  26. })
  27. }
  28. def sessionTokenPair(implicit request: RequestHeader): Option[RequestToken] = {
  29. for {
  30. token <- request.session.get("token")
  31. secret <- request.session.get("secret")
  32. } yield {
  33. RequestToken(token, secret)
  34. }
  35. }
  36. }
  1. object Application extends Controller {
  2. def timeline = Action.async { implicit request =>
  3. Twitter.sessionTokenPair match {
  4. case Some(credentials) => {
  5. WS.url("https://api.twitter.com/1.1/statuses/home_timeline.json")
  6. .sign(OAuthCalculator(Twitter.KEY, credentials))
  7. .get
  8. .map(result => Ok(result.json))
  9. }
  10. case _ => Future.successful(Redirect(routes.Twitter.authenticate))
  11. }
  12. }
  13. }