#概要
Scalaで, GET https://api.twitter.com/1.1/statuses/user_timeline.json?count=1&user_id=XXX
というリクエストをするために,以下のコードを書いたところ,401 Authorization requiredというレスポンスが返ってきた.
#環境
- macOS Sierra 10.12.2
- Scala 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
に次のコードを書きました:
scala:hoge.scala
1import scala.concurrent.ExecutionContext.Implicits.global 2import java.net.URLEncoder 3import javax.crypto.spec.SecretKeySpec 4import javax.crypto.Mac 5import play.api.libs.json._ 6import play.api.libs.ws.ning.NingWSClient 7import org.apache.commons.codec.binary.Base64 8 9object Hoge { 10 def main(args: Array[String]) = { 11 val consumerKey = "AAA" 12 val consumerSecret = "BBB" 13 val token = "CCC" 14 val tokenSecret = "DDD" 15 val timestamp = (System.currentTimeMillis / 1000).toString 16 val nonce = System.nanoTime.toString 17 val id = "XXX" 18 19 val host = "https://api.twitter.com" 20 val path = "/1.1/statuses/user_timeline.json" 21 val url = host + path 22 23 val query = Map( 24 "count" -> "1", 25 "user_id" -> id 26 ) 27 28 val sign = signature( 29 url, consumerKey, consumerSecret, token, tokenSecret, timestamp, nonce, query 30 ) 31 val header = Map( 32 "Content-Type" -> "application/x-www-form-urlencoded;charset=UTF-8", 33 "Authorization" -> oauthHeader(consumerKey, token, sign, timestamp, nonce) 34 ) 35 36 val ws = NingWSClient() 37 val res = get(ws)(url, query, header) 38 Thread.sleep(1000) 39 res.value.get.get.json 40 ws.close() 41 } 42 43 def signature(url: String, consumerKey: String, consumerSecret: String, token: String, tokenSecret: String, 44 timestamp: String, nonce: String, query: Map[String, String]) = { 45 val params = Map( 46 "oauth_consumer_key" -> consumerKey, 47 "oauth_nonce" -> nonce, 48 "oauth_signature_method" -> "HMAC-SHA1", 49 "oauth_timestamp" -> timestamp, 50 "oauth_token" -> token, 51 "oauth_version" -> "1.0" 52 ) ++ query 53 val paramsEncoded = params.toList.sorted map { case (k, v) => urlencode(k) + "=" + urlencode(v) } 54 val paramString = paramsEncoded mkString "&" 55 val signatureBase = "GET&" + urlencode(url) + "&" + urlencode(paramString) 56 val signingKey = urlencode(consumerSecret) + "&" + urlencode(tokenSecret) 57 base64encode(encry(signingKey, signatureBase)) 58 } 59 60 def oauthHeader(consumerKey: String, token: String, sign: String, timestamp: String, nonce: String) = { 61 val params = Map( 62 "oauth_consumer_key" -> consumerKey, 63 "oauth_nonce" -> nonce, 64 "oauth_signature" -> sign, 65 "oauth_signature_method" -> "HMAC-SHA1", 66 "oauth_timestamp" -> timestamp, 67 "oauth_token" -> token, 68 "oauth_version" -> "1.0" 69 ).toList.sorted 70 "OAuth " + (params map { case (k, v) => urlencode(k) + "=\"" + urlencode(v) + "\"" } mkString ", ") 71 } 72 73 def urlencode(s: String) = { 74 URLEncoder.encode(s, "UTF-8") 75 } 76 77 def base64encode(s: String) = { 78 Base64.encodeBase64String(s.getBytes("UTF-8")) 79 } 80 81 def encry(key: String, data: String) = { 82 val secret = new SecretKeySpec(key.getBytes, "HmacSHA1") 83 val mac = Mac.getInstance("HmacSHA1") 84 mac.init(secret) 85 val res = mac.doFinal(data.getBytes("UTF-8")) 86 res map { r => "%02x".format(r) } mkString "" 87 } 88 89 def get(ws: NingWSClient)(url: String, query: Map[String, String], header: Map[String, String]) = { 90 ws.url(url).withQueryString(query.toList: _*).withHeaders(header.toList: _*).get 91 } 92}
これをsbt compile
,sbt run
で実行したら,上述の401が返ってきました
val ws = NingWSClient
以下は問題ないと思います.他の(OAuth1.0認証を用いない)GETやPOSTをした場合は,正常なレスポンスが返ってきました.
consumer keyやtokenなども間違ってはいないです.Rubyで同様の処理をしたら,問題なく実行されました.
署名の作成手順は,公式ドキュメントを何度も読みながら書いたので,間違いないと思います.
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"
なので,暗号化,エンコード(urlencode
,base64encode
,encry
)のあたりがおかしいのだと思いますが,どうなのでしょう.
Scalaの練習のために作っているので,出来る限りTwitter4Jのようなものは使いたくないです.
どこを修正したらよいのか教えてください.
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。