如何在Scala Play 2.4.X中验证OAuth签名

时间:2015-06-02 03:10:32

标签: scala oauth playframework playframework-2.0

要与服务集成,我需要在Play 2.4(scala)服务器上实现REST端点。该服务向我发送了一份GET请求,该请求是用他们给我的密钥和秘密签署的。

我需要验证请求是否来自他们

我的方法是:

val accessTokenOpt: Option[String] = request.headers.get(Security.AUTH_HEADER)
(accessTokenOpt, Security.consumer) match {
  case (Some(accessToken), Some(consumer)) => {
    val calculator = new OAuthCalculator(consumer, RequestToken("", ""))
    val expected = WS.url(s"http://${request.host}${request.uri}").sign(calculator)
    (expected.headers.get(Security.AUTH_HEADER), accessToken) match {
      case (Some(exp), Some(actual)) if exp == actual =>
        //good
      case (Some(exp), Some(actual)) =>
        //mismatch
      case (e, a) =>
        //missing tokens. :(
    }
  }
  case _ => //bad
}

问题是expected.headers.(Security.AUTH_HEADER)返回None

我错过了代码中的小东西,还是我的整个方法都错了?

1 个答案:

答案 0 :(得分:0)

我认为您的方法是正确的,并且从完全相同的参数计算的oauth签名将始终匹配。但是你必须考虑一些像nonce和timestamp这样的参数,为了做到这一点,我必须直接使用签名的Java类。

以下是我设法让它发挥作用的方式。请记住,在我的情况下,与我的REST端点联系的外部服务发送的Authorization标头包含用于计算oauth_signature值的所有信息,除了我拥有的oauth秘密。

private def getProtocolFromRequest[T](request: Request[T]): String = {
  // To handle the case where a SSL offloading is involved
  request.headers.get(HeaderNames.X_FORWARDED_PROTO) match {
    case Some(forwardedProto: String) => forwardedProto
    case _ => if(request.secure) "https" else "http"
  }
}

def isOAuthSignatureValid[T](request:  Request[T]): Boolean = {
  request.headers.get(com.ning.http.client.oauth.OAuthSignatureCalculator.HEADER_AUTHORIZATION) match {
    case Some(authorizationHeaders) =>
      val AuthRegex = ".*oauth_nonce=\"([^\"]*)\".*oauth_signature=\"([^\"]*)\".*oauth_timestamp=\"([^\"]*)\".*".r

      authorizationHeaders match {
        case AuthRegex(nonce: String, providedSignature: String, timestamp: String) =>
          val signatureCalculator = new OAuthSignatureCalculator(new com.ning.http.client.oauth.ConsumerKey(oauthKey, oauthSecret), new com.ning.http.client.oauth.RequestToken(null, ""))
          val params = request.queryString.map(query => new Param(query._1, query._2.head)).toSeq
          val protocol = getProtocolFromRequest(request)
          val url = s"$protocol://${request.host}${request.path}"

          val expectedSignature = signatureCalculator.calculateSignature(
            request.method,
            Uri.create(url),
            timestamp.toInt,
            nonce,
            new util.ArrayList(),
            scala.collection.JavaConversions.seqAsJavaList(params)
          )
          expectedSignature.equals(URLDecoder.decode(providedSignature, "UTF-8"))
        case _ => false
      }
    case _ => false
  }
}

对我有用的流程如下:

  1. 获取授权标题
  2. 通过提供您的oauth使用者密钥和密钥来创建OAuthSignatureCalculator。在我的情况下,我没有使用任何请求令牌。
  3. 从授权标头中提取所需的oauth参数:signature,nonce,timestamp
  4. 使用OAuthSignatureCalculator计算具有完全相同的随机数和时间戳的签名
  5. 将提供的签名与计算的签名进行比较。如果它们匹配,则呼叫者将被认证,否则,它不会。
  6. 希望这可以帮助某人