我尝试创建一个应用程序,自动向 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 方法上(?)