我想做的是在浏览器中执行 JavaScript 代码,向 Roblox API 发送 GET 请求https://economy.roblox.com/v1/user/currency 获取 Robux(游戏内货币)我的 Roblox 帐户中的游戏 Roblox)金额。问题是,当我打开 roblox.com 的选项卡并执行 JavaScript 代码来获取 Robux 金额时,对 API 的请求将不会经过身份验证。 (请求中缺少 .ROBLOSECURITY 会话 cookie)
问题是,打开economic.roblox.com 选项卡将允许对 /v1/user/currency 的请求进行身份验证,但我需要在 roblox.com 网站上执行我的任务。在 roblox.com 中时,有什么方法可以向economic.roblox.com 发送经过身份验证的请求吗?
不,我不能只获取我帐户的 .ROBLOSECURITY cookie,然后手动将其包含在 JavaScript 代码中以将其发送到 API,这是我不能这样做的原因。
我想到的一种方法是让 JavaScript 代码重定向我或打开economic.roblox.com 的选项卡一瞬间发送 API 请求,然后重定向回我所在的 roblox.com 网址,但是这不起作用,因为如果我被重定向到另一个网址,正常的浏览器行为会阻止其余 JavaScript 代码的执行。
过去几天我一直在解决类似的问题,这是我提出的解决方案(完全归功于 Github 上的Julli4n 的蝙蝠生成代码)
javascript:(
function(){
function arrayBufferToBase64String(arrayBuffer){
let res = "";
const bytes = new Uint8Array(arrayBuffer);
for (let i = 0; i < bytes.byteLength; i++) {
res += String.fromCharCode(bytes[i]);
};
return btoa(res);
};
async function hashStringSha256(str){
const uint8 = new TextEncoder().encode(str);
const hashBuffer = await crypto.subtle.digest("SHA-256", uint8);
return arrayBufferToBase64String(hashBuffer);
};
async function signWithKey(privateKey, data){
const bufferResult = await crypto.subtle.sign(
{name: "ECDSA",hash: { name: "SHA-256" }},
privateKey,
new TextEncoder().encode(data).buffer
);
return arrayBufferToBase64String(bufferResult);
};
async function getCryptoKeyPairFromDB(dbName,dbObjectName,dbObjectChildId){
let targetVersion = 1;
/*we want Roblox to create the DB on their end, so we do not want to interfere*/
if ("databases" in indexedDB) {
const databases = await indexedDB.databases();
const database = databases.find((db) => db.name === dbName);
if (!database) {
return null;
}
if (database?.version) {
targetVersion = database.version;
}
};
return new Promise((resolve, reject) => {
const request = indexedDB.open(dbName,targetVersion);
request.onsuccess = () => {
try {
const db = request.result;
const transaction = db.transaction(dbObjectName, "readonly");
const objectStore = transaction.objectStore(dbObjectName);
const get = objectStore.get(dbObjectChildId);
get.onsuccess = () => {
resolve(get.result);
};
get.onerror = () => {
reject(request.error);
};
transaction.oncomplete = () => {
db.close();
};
} catch (err) {
reject(err);
}
};
request.onerror = () => {
reject(request.error);
}
})
};
async function generateBAT(body){
const pair = await getCryptoKeyPairFromDB("hbaDB","hbaObjectStore","hba_keys");
if (!pair?.privateKey) {
return null;
};
const timestamp = Math.floor((Date.now()+1000) / 1000).toString();
let strBody;
if (typeof body === "object") {
strBody = JSON.stringify(body);
} else if (typeof body === "string") {
strBody = body;
};
const hashedBody = await hashStringSha256(strBody);
const payloadToSign = [hashedBody, timestamp].join("|");
const signature = await signWithKey(pair.privateKey, payloadToSign);
return [hashedBody, timestamp, signature].join("|");
};
/*-------------*/
token = document.getElementsByName("csrf-token")[0].dataset.token;
body = "";
obody = "";
headers = {
"x-csrf-token":token,
"x-bound-auth-token" : "",
"accept":"application/json"};
bat = generateBAT(body);
bat.then((tok) => {
headers["x-bound-auth-token"] = tok
});
const fetchPromise = fetch("https://economy.roblox.com/v1/user/currency",
{"headers":headers,
"method":"GET",
"credentials": "include"}
);
fetchPromise
.then((response) => {
if (!response.ok) {
heastr = "";
token = response.headers.get("x-csrf-token");
throw new Error(`HTTP error: ${response.status+response.statusText}`);
};
return response.json();
})
.then((data) => {
alert(JSON.stringify(data));
})
.catch((error) => {
alert(`Could not get... anything: ${error}`);
alert(JSON.stringify(headers));
alert(body);
});
}());