我在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
}
}
timestamp
,nonce
,sign
和oauthHeader
在這種情況下是
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。
請告訴我是什麼問題。