2016-12-24 60 views
2

我在Scala上嘗試過GET https://api.twitter.com/1.1/statuses/user_timeline.json?count=1&user_id=XXX,但是我得到了一個錯誤401 Authorization required如何用Scala上的Twitter OAuth 1.0授權我自己

這裏是我的規格:

  • MACOS塞拉利昂10.12.2
  • 斯卡拉2.12.1
  • SBT 0.13.13

build.sbt

name := "hoge" 

version := "1.0" 

libraryDependencies ++= Seq(
    "com.typesafe.play" %% "play-ws" % "2.4.0-M2", 
    "commons-codec" % "commons-codec" % "1.3.0" 
) 

而且hoge.scala

import scala.concurrent.ExecutionContext.Implicits.global 
import java.net.URLEncoder 
import javax.crypto.spec.SecretKeySpec 
import javax.crypto.Mac 
import play.api.libs.json._ 
import play.api.libs.ws.ning.NingWSClient 
import org.apache.commons.codec.binary.Base64 

object Hoge { 
    def main(args: Array[String]) = { 
    val consumerKey = "AAA" 
    val consumerSecret = "BBB" 
    val token = "CCC" 
    val tokenSecret = "DDD" 
    val timestamp = (System.currentTimeMillis/1000).toString 
    val nonce = System.nanoTime.toString 
    val id = "XXX" 

    val host = "https://api.twitter.com" 
    val path = "/1.1/statuses/user_timeline.json" 
    val url = host + path 

    val query = Map(
     "count" -> "1", 
     "user_id" -> id 
    ) 

    val sign = signature(
     url, consumerKey, consumerSecret, token, tokenSecret, timestamp, nonce, query 
    ) 
    val header = Map(
     "Content-Type" -> "application/x-www-form-urlencoded;charset=UTF-8", 
     "Authorization" -> oauthHeader(consumerKey, token, sign, timestamp, nonce) 
    ) 

    val ws = NingWSClient() 
    val res = get(ws)(url, query, header) 
    Thread.sleep(3000) 
    res.value.get.get.json 
    ws.close() 
    } 

    def signature(url: String, consumerKey: String, consumerSecret: String, token: String, tokenSecret: String, timestamp: String, nonce: String, query: Map[String, String]) = { 
    val params = Map(
     "oauth_consumer_key" -> consumerKey, 
     "oauth_nonce" -> nonce, 
     "oauth_signature_method" -> "HMAC-SHA1", 
     "oauth_timestamp" -> timestamp, 
     "oauth_token" -> token, 
     "oauth_version" -> "1.0" 
    ) ++ query 
    val paramsEncoded = params.toList.sorted map { case (k, v) => urlencode(k) + "=" + urlencode(v) } 
    val paramString = paramsEncoded mkString "&" 
    val signatureBase = "GET&" + urlencode(url) + "&" + urlencode(paramString) 
    val signingKey = urlencode(consumerSecret) + "&" + urlencode(tokenSecret) 
    base64encode(encry(signingKey, signatureBase)) 
    } 

    def oauthHeader(consumerKey: String, token: String, sign: String, timestamp: String, nonce: String) = { 
    val params = Map(
     "oauth_consumer_key" -> consumerKey, 
     "oauth_nonce" -> nonce, 
     "oauth_signature" -> sign, 
     "oauth_signature_method" -> "HMAC-SHA1", 
     "oauth_timestamp" -> timestamp, 
     "oauth_token" -> token, 
     "oauth_version" -> "1.0" 
    ).toList.sorted 
    "OAuth " + (params map { case (k, v) => urlencode(k) + "=\"" + urlencode(v) + "\"" } mkString ", ") 
    } 

    def urlencode(s: String) = { 
    URLEncoder.encode(s, "UTF-8") 
    } 

    def base64encode(s: String) = { 
    Base64.encodeBase64String(s.getBytes("UTF-8")) 
    } 

    def encry(key: String, data: String) = { 
    val secret = new SecretKeySpec(key.getBytes, "HmacSHA1") 
    val mac = Mac.getInstance("HmacSHA1") 
    mac.init(secret) 
    val res = mac.doFinal(data.getBytes("UTF-8")) 
    res map { r => "%02x".format(r) } mkString "" 
    } 

    def get(ws: NingWSClient)(url: String, query: Map[String, String], header: Map[String, String]) = { 
    ws.url(url).withQueryString(query.toList: _*).withHeaders(header.toList: _*).get 
    } 
} 

timestampnoncesignoauthHeader在這種情況下是

timestamp = 1482542041 
nonce = 214129951907166 
sign = YWQ2Y2VjNzJkOTQwY2JhYjEzYzgxZDM2YTg3ZGNlNjQ3ZjIzMDQ4Yw== 
oauthHeader = OAuth oauth_consumer_key="AAA", oauth_nonce="214129951907166", oauth_signature="YWQ2Y2VjNzJkOTQwY2JhYjEzYzgxZDM2YTg3ZGNlNjQ3ZjIzMDQ4Yw%3D%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1482542041", oauth_token="CCC", oauth_version="1.0" 

我做sbt compile然後sbt run,並得到了響應401 Authorization required

我認爲在val ws = NingWSClient()ws.close()之間沒有問題:當我發送不需要OAuth 1.0的GET/POST請求(例如Google API)時,我成功了。

我也認爲消費者密鑰,消費者機密,令牌,令牌密碼是正確的:當我在Ruby上運行類似的進程時,一切正常。

我認爲簽名過程是正確的,因爲我在文檔中反覆檢查過它。

所以,我認爲編碼或加密他們是錯誤的。 我不想使用庫Twitter4J,因爲我只是在學習Scala。

請告訴我是什麼問題。

回答

0

URLEncoder.encode將空格變成'+'而不是'%20',所以放replace("+", "%20")。 加密成爲:

val hmacShai = "HmacSHA1" 
val keyStr = new SecretKeySpec(bytes(key), hmacShai) 
val sig = { 
    val mac = Mac.getInstance(hmacShai) 
    mac.init(keyStr) 
    new String(base64encode(mac.doFinal(bytes(data)))) 
} 
相關問題