如何使用Scala上的Twitter OAuth 1.0授权自己

时间:2016-12-24 05:25:55

标签: scala twitter oauth

我在Scala上尝试了mklink .htaccess C:\path\outside\.htaccess mklink /D .upload C:\path\outside\upload ,但收到了错误GET https://api.twitter.com/1.1/statuses/user_timeline.json?count=1&user_id=XXX

这是我的规格:

  • macOS Sierra 10.12.2
  • Scala 2.12.1
  • sbt 0.13.13

401 Authorization required

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 } } timestampnoncesign

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 requiredval ws = NingWSClient()之间没有问题:当我发送不需要OAuth 1.0的GET / POST请求(例如Google API)时,我成功了。

我还认为消费者密钥,消费者秘密,令牌,令牌秘密是正确的:当我在Ruby上运行类似的过程时,一切都很好。

我认为签名的过程是正确的,因为我一遍又一遍地在文档中检查过它。

所以,我认为编码或加密它们是错误的。 我不想使用Twitter4J库,因为我只是在研究Scala。

请告诉我这是什么问题。

1 个答案:

答案 0 :(得分: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))))
}