8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

带有 Scala 和 sttp 的 OAuth 1.0 和 X API v2

amihart 1月前

13 0

我尝试创建一个应用程序,自动向 X 发送国际象棋锦标赛公告。除了 OAuth-1.0 授权之外,一切都按预期运行。我跟随 Kevin Williams。HMAC-SHA1 由 Aravind_G 提供...

我尝试创建一个应用程序,自动向 X 发送国际象棋锦标赛公告。除了 OAuth-1.0 授权之外,一切都按预期运行。我跟随 Kevin Williams。HMAC -SHA1 由 Aravind_G

这是代码。我省略了 JSON 部分,但效果很好。请相信我以正确的方式提供了原始凭证 :-)

package de.qno.tournamentadmin

import org.joda.time.DateTime
import sttp.client4.*
import sttp.model.*

import java.net.URLEncoder
import javax.crypto.spec.SecretKeySpec
import scala.util.Random

[…]
/**
 * With thx to Kevin Williams
 * https://medium.com/@kevinwilliams.dev/posting-to-x-twitter-with-oauth-1-0-8d4de172cfa6
 */
object Auth:
  def urlEncode(text: String): String =
    URLEncoder.encode(text, java.nio.charset.Charset.defaultCharset())

  def generateNonce: String =
    Random.alphanumeric.take(15).mkString

  def sortData(data: Map[String, String]): Seq[(String, String)] =
    data.toSeq.sortBy(_._1) // sort alphabetically
  
  def reformatData(data: Seq[(String, String)]): Seq[String] =   
    data.map(x => s"${x._1}=${x._2}")
      
  def reformatSignedData(data: Seq[(String, String)]): Seq[String] =
    data.map(x => s"${x._1}=\"${urlEncode(x._2)}\"")  

  /**
   * With thx to Aravind_G
   * https://community.gatling.io/t/hmac-sha1-signature-generation-using-scala/5844
   * @param text
   * @return
   */
  def sha1sign (text: String):  String =
    val shaHash = javax.crypto.Mac.getInstance("HmacSHA1")
    val secretKey = s"${urlEncode(TournamentAdmin.xApiKeySecret)}&${urlEncode(TournamentAdmin.xAccessTokenSecret)}"
    val signingKey = new SecretKeySpec(secretKey.getBytes, "HmacSHA1")
    shaHash.init(signingKey)
    val signatureHash = shaHash.doFinal(text.getBytes("UTF-8"))
    val baseEncoder = java.util.Base64.getEncoder
    val signatureString = (for byte <- signatureHash yield f"$byte%02X").mkString
    baseEncoder.encodeToString(signatureString.getBytes)
  
  def signedHeader: Header =
    val collectAuthHeaderMap = Map(
      "oauth_consumer_key" -> TournamentAdmin.xApiKey,
      "oauth_signature_method" -> "HMAC-SHA1",
      "oauth_timestamp" -> DateTime().getMillis.toString,
      "oauth_nonce" -> generateNonce,
      "oauth_token" -> TournamentAdmin.xAccesToken,
      "oauth_version" -> "1.0"
    )

    val sortedData = sortData(collectAuthHeaderMap)
    val reformattedData = reformatData(sortedData)
    val oneStringData = "POST" + "&" + urlEncode(Twitter.createPostEndpoint) + "&" + urlEncode(reformattedData.mkString("&"))
    val signature = sha1sign(oneStringData)
    val signatureDataTupel: (String, String) = ("oauth_signature", signature) // add signature to data
    val sortedSignedHeader = sortData(collectAuthHeaderMap + signatureDataTupel)
    val reformattedSignedHeader = reformatSignedData(sortedSignedHeader)
    val oneStringSignedHeader = "OAuth " + reformattedSignedHeader.mkString(", ")
    Header("Authorization", oneStringSignedHeader)

object Twitter:
  val createPostEndpoint = "https://api.x.com/2/tweets"

  def createPost(text: String): XCreatePostResponse =
    val textObj = XCreatePostBody(text)
    val h = Auth.signedHeader
    println(h)
    basicRequest
      .header(h)
      .contentType("application/json")
      .body(write(textObj))
      .post(uri"$createPostEndpoint")
      .response(asJson[XCreatePostResponse].getRight)
      .send(DefaultSyncBackend())
      .body

  @main
  def showHeader: Unit =
    createPost("Test")

作为对主要方法的响应,我得到了一个带有 JSON 响应的 401:

{
  "title": "Unauthorized",
  "type": "about:blank",
  "status": 401,
  "detail": "Unauthorized"
}

创建的标头:

Authorization: OAuth oauth_consumer_key="xxx", oauth_nonce="nae0Pk46M7ox3yP", oauth_signature="NjM5NDZERkIxRUY1NEY3OUE4Qzc2MjY5ODI0NEYyM0NFNjE0RjZDMg%3D%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1726259880224", oauth_token="xxx", oauth_version="1.0"

我认为我编码了不应该编码的内容。但我不知道我哪里出错了。谁能告诉我我的方法有什么错误?也许有人知道一个教程,可以展示如何创建纯文本标头?

TIA、Q 号

编辑:在第一个版本中,我省略了必要的步骤。我希望我完成了所有事情。

编辑:在第二次编辑中,我修复了一些自我检测的错误。现在我有一个正确格式的授权标头。所以问题一定是出在 sha1sign 方法上(?)

帖子版权声明 1、本帖标题:带有 Scala 和 sttp 的 OAuth 1.0 和 X API v2
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由amihart在本站《scala》版块原创发布, 转载请注明出处!
最新回复 (0)
返回
作者最近主题: