【问题标题】:Play Framework [2.4.x] OpenID and Oauth -- using PLAINTEXT for oauth_signature_methodPlay Framework [2.4.x] OpenID 和 Oauth -- 将 PLAINTEXT 用于 oauth_signature_method
【发布时间】:2015-08-05 03:56:11
【问题描述】:

基于此处的文档:https://www.playframework.com/documentation/2.4.x/ScalaOAuth

我有一个 Twitter“阅读器”可以正常工作。我可以授权“我的应用”并按预期通过 API 进行调用。

我正在尝试对 FreshBooks 进行类似的处理,但我似乎遇到了将 oauth_signature_method 预设为 HMAC-SHA1 的问题。

由于供应商对所有 API 调用强制执行 HTTPS,它们(仅)似乎支持 oauth_signature_method 的 PLAINTEXT

正在与供应商合作以确认这一点。

搜索文档和代码以查看签名方法是否可以以某种方式选择,如果可以,我何时/在何处将更改注入代码?

如果有其他人遇到此问题并找到解决方案,将不胜感激!

【问题讨论】:

  • 现在的问题是:我们如何“设置”其中之一:import oauth.signpost.signature.AuthorizationHeaderSigningStrategy;导入 oauth.signpost.signature.HmacSha1MessageSigner;导入 oauth.signpost.signature.OAuthMessageSigner; 导入 oauth.signpost.signature.PlainTextMessageSigner; 导入 o​​auth.signpost.signature.QueryStringSigningStrategy;导入 oauth.signpost.signature.SigningStrategy;

标签: oauth playframework-2.0 openid


【解决方案1】:

似乎有 2/3s 的解决方案是 hack 代码如下:

  val serviceInfo = ServiceInfo(
      "https://sample.freshbooks.com/oauth/oauth_request.php",
      "https://sample.freshbooks.com/oauth/oauth_access.php",
      "https://sample.freshbooks.com/oauth/oauth_authorize.php", KEY)

  val provider = new CommonsHttpOAuthProvider(serviceInfo.requestTokenURL, serviceInfo.accessTokenURL, serviceInfo.authorizationURL)

  val consumer = new DefaultOAuthConsumer(serviceInfo.key.key, serviceInfo.key.secret)

  consumer.setMessageSigner(new PlainTextMessageSigner())

  def retrieveAccessToken(token: RequestToken, verifier: String): Either[OAuthException, RequestToken] = {
          consumer.setTokenWithSecret(token.token, token.secret)
          try {
              provider.retrieveAccessToken(consumer, verifier);
              Right(new RequestToken(consumer.getToken(), consumer.getTokenSecret()))
          } catch {
              case ex: OAuthException => Left(ex)
          }
      }

      def retrieveRequestToken(callbackURL: String): Either[OAuthException, RequestToken] = {
        try {
            provider.retrieveRequestToken(consumer, callbackURL)
            Right(new RequestToken(consumer.getToken(), consumer.getTokenSecret()))
        } catch {
            case ex: OAuthException => Left(ex)
        }
      }

我说 2/3s 因为最后一块拼图使用了OAuthCalculator

    WS.url("https://sample.freshbooks.com/api/2.1/xml-in")
      .sign(OAuthCalculator(KEY, credentials))
      .post(data)
      .map(result => Ok(result.body))
  }

我找不到改变它的方法——我可能不得不重写整个类或创建一个新类。奇怪的是,我们有两种方法以一种方式做基本相同的事情,而另一种方法则完全不同。

这是一个完全的黑客,因为我们应该能够把

consumer.setMessageSigner(new PlainTextMessageSigner()) 

直接进入初始代码

【讨论】:

    【解决方案2】:

    我在 github 上发布了一个可行的解决方案:https://github.com/techmag/OAuthPlainTextCalculator

    为方便起见,我将在此处重复代码:

        class OAuthPlainTextCalculator(consumerKey: ConsumerKey, requestToken: RequestToken) extends WSSignatureCalculator with com.ning.http.client.SignatureCalculator {
      import com.ning.http.client.{ Request, RequestBuilderBase }
      import com.ning.http.util.UTF8UrlEncoder
      import com.ning.http.util.Base64
    
      val HEADER_AUTHORIZATION = "Authorization"
      val KEY_OAUTH_CONSUMER_KEY = "oauth_consumer_key"
      val KEY_OAUTH_NONCE = "oauth_nonce"
      val KEY_OAUTH_SIGNATURE = "oauth_signature"
      val KEY_OAUTH_SIGNATURE_METHOD = "oauth_signature_method"
      val KEY_OAUTH_TIMESTAMP = "oauth_timestamp"
      val KEY_OAUTH_TOKEN = "oauth_token"
      val KEY_OAUTH_VERSION = "oauth_version"
    
      val OAUTH_VERSION_1_0 = "1.0"
      val OAUTH_SIGNATURE_METHOD = "PLAINTEXT"
    
      protected final val nonceBuffer: Array[Byte] = new Array[Byte](16)
    
      override def calculateAndAddSignature(request: Request, requestBuilder: RequestBuilderBase[_]): Unit = {
        val nonce: String = generateNonce
        val timestamp: Long = System.currentTimeMillis() / 1000L
        val signature = calculateSignature(request.getMethod, request.getUrl, timestamp, nonce, request.getFormParams, request.getQueryParams)
        val headerValue = constructAuthHeader(signature, nonce, timestamp)
        requestBuilder.setHeader(HEADER_AUTHORIZATION, headerValue);
      }
    
      /**
       * from http://oauth.net/core/1.0/#signing_process
       * oauth_signature is set to the concatenated encoded values of the
       * Consumer Secret and Token Secret,
       * separated by a ‘&’ character (ASCII code 38),
       * even if either secret is empty.
       * The result MUST be encoded again.
       */
    
      def calculateSignature(method: String, baseURL: String, oauthTimestamp: Long, nonce: String, formParams: java.util.List[com.ning.http.client.Param], queryParams: java.util.List[com.ning.http.client.Param]) = {
        val signedText = new StringBuilder(100)
        signedText.append(consumerKey.secret)
        signedText.append('&');
        signedText.append(requestToken.secret)
        UTF8UrlEncoder.encode(signedText.toString)
      }
    
      def constructAuthHeader(signature: String, nonce: String, oauthTimestamp: Long, sb: StringBuilder = new StringBuilder) = {
        constructAuthHeader_sb(signature, nonce, oauthTimestamp).toString
      }
    
      def constructAuthHeader_sb(signature: String, nonce: String, oauthTimestamp: Long, sb: StringBuilder = new StringBuilder(250)) = {
        sb.synchronized {
          sb.append("OAuth ")
    
          sb.append(KEY_OAUTH_CONSUMER_KEY)
          sb.append("=\"")
          sb.append(consumerKey.key)
          sb.append("\", ")
    
          sb.append(KEY_OAUTH_TOKEN)
          sb.append("=\"")
          sb.append(requestToken.token)
          sb.append("\", ")
    
          sb.append(KEY_OAUTH_SIGNATURE_METHOD)
          sb.append("=\"")
          sb.append(OAUTH_SIGNATURE_METHOD)
          sb.append("\", ")
    
          // careful: base64 has chars that need URL encoding:
          sb.append(KEY_OAUTH_SIGNATURE)
          sb.append("=\"");
          sb.append(signature)
          sb.append("\", ")
    
          sb.append(KEY_OAUTH_TIMESTAMP)
          sb.append("=\"")
          sb.append(oauthTimestamp)
          sb.append("\", ")
    
          // also: nonce may contain things that need URL encoding (esp. when using base64):
          sb.append(KEY_OAUTH_NONCE)
          sb.append("=\"");
          sb.append(UTF8UrlEncoder.encode(nonce))
          sb.append("\", ")
    
          sb.append(KEY_OAUTH_VERSION)
          sb.append("=\"")
          sb.append(OAUTH_VERSION_1_0)
          sb.append("\"")
          sb
        }
      }
    
      def generateNonce = synchronized {
        scala.util.Random.nextBytes(nonceBuffer)
        // let's use base64 encoding over hex, slightly more compact than hex or decimals
        Base64.encode(nonceBuffer)
      }
    }
    
    object OAuthPlainTextCalculator {
      def apply(consumerKey: ConsumerKey, token: RequestToken): WSSignatureCalculator = {
        new OAuthPlainTextCalculator(consumerKey, token)
      }
    }
    

    【讨论】:

    • 请注意 github repo ha 更新,而随着时间的推移,这个答案可能不会 - 如果您想使用代码,请参阅 repo。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-16
    • 1970-01-01
    • 2015-10-08
    • 1970-01-01
    • 2016-07-02
    • 2015-11-05
    相关资源
    最近更新 更多