🌱 成功获取扫码登录返回 tokens

This commit is contained in:
BTMuli
2023-08-31 17:13:46 +08:00
parent cf691d8b51
commit a3c1442c7d
6 changed files with 298 additions and 1 deletions

View File

@@ -55,7 +55,9 @@
"js-md5": "^0.7.3",
"pinia": "^2.1.6",
"pinia-plugin-persistedstate": "^3.2.0",
"qrcode.vue": "^3.4.1",
"tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql#v1",
"uuid": "^9.0.0",
"vue": "^3.3.4",
"vue-echarts": "^6.6.1",
"vue-json-viewer": "^3.0.4",

19
pnpm-lock.yaml generated
View File

@@ -29,9 +29,15 @@ dependencies:
pinia-plugin-persistedstate:
specifier: ^3.2.0
version: 3.2.0(pinia@2.1.6)
qrcode.vue:
specifier: ^3.4.1
version: 3.4.1(vue@3.3.4)
tauri-plugin-sql-api:
specifier: github:tauri-apps/tauri-plugin-sql#v1
version: git@github.com+tauri-apps/tauri-plugin-sql/b8fd19dac907cc8c3d78681cd4803a326b8b861e
uuid:
specifier: ^9.0.0
version: 9.0.0
vue:
specifier: ^3.3.4
version: 3.3.4
@@ -3756,6 +3762,14 @@ packages:
engines: {node: '>=6'}
dev: true
/qrcode.vue@3.4.1(vue@3.3.4):
resolution: {integrity: sha512-wq/zHsifH4FJ1GXQi8/wNxD1KfQkckIpjK1KPTc/qwYU5/Bkd4me0w4xZSg6EXk6xLBkVDE0zxVagewv5EMAVA==}
peerDependencies:
vue: ^3.0.0
dependencies:
vue: 3.3.4
dev: false
/qs@6.11.0:
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
engines: {node: '>=0.6'}
@@ -4684,6 +4698,11 @@ packages:
base64-arraybuffer: 1.0.2
dev: false
/uuid@9.0.0:
resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==}
hasBin: true
dev: false
/validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
dependencies:

View File

@@ -29,6 +29,7 @@
"https://bbs.mihoyo.com/*",
"https://hk4e-api.mihoyo.com/*",
"https://passport-api.mihoyo.com/*",
"https://passport-api.miyoushe.com/*",
"https://passport-api-v4.mihoyo.com/*",
"https://act-webstatic.mihoyo.com/*",
"https://sdk-webstatic.mihoyo.com/*",

View File

@@ -1,6 +1,57 @@
<template>
<div>
<h1>Test</h1>
<div class="btn-list">
<v-btn @click="renderQr">渲染二维码</v-btn>
<v-btn @click="getData">获取数据</v-btn>
</div>
<div class="qr-container">
<qrcode-vue :value="content" :size="size" level="H" render-as="svg" />
</div>
</div>
</template>
<script lang="ts" setup></script>
<script lang="ts" setup>
// vue
import { ref } from "vue";
import QrcodeVue from "qrcode.vue";
import showSnackbar from "../../components/func/snackbar";
// utils
import { getLoginQr, getLoginStatus } from "../../plugins/Mys/utils/doWebLogin";
const content = ref<string>("qrcode");
const size = ref<number>(300);
const ticket = ref<string>("");
async function renderQr(): Promise<void> {
const res = await getLoginQr();
if ("retcode" in res) {
showSnackbar({
text: `[${res.retcode}] ${res.message}`,
color: "error",
});
return;
}
ticket.value = res.ticket;
content.value = res.url;
}
async function getData(): Promise<void> {
const res = await getLoginStatus(ticket.value);
if ("retcode" in res) {
showSnackbar({
text: `[${res.retcode}] ${res.message}`,
color: "error",
});
return;
}
console.log(res);
}
</script>
<style lang="css" scoped>
.btn-list {
display: flex;
justify-content: flex-start;
margin-bottom: 20px;
gap: 10px;
}
</style>

134
src/plugins/Mys/types/WebLogin.d.ts vendored Normal file
View File

@@ -0,0 +1,134 @@
/**
* @file plugins Mys types WebLogin.d.ts
* @description Mys 插件 Web 登录类型定义文件
* @author BTMuli <bt-muli@outlook.com>
* @since Beta v0.3.0
*/
/**
* @description Mys 插件 Web 登录类型
* @since Beta v0.3.0
* @namespace WebLogin
* @return WebLogin
*/
declare namespace TGApp.Plugins.Mys.WebLogin {
/**
* @description 获取登录二维码返回数据
* @since Beta v0.3.0
* @interface GetLoginQrResponse
* @extends TGApp.Plugins.Mys.Base.Response
* @property {GetLoginQrData} data 数据
* @return GetLoginQrResponse
*/
export interface GetLoginQrResponse extends TGApp.Plugins.Mys.Base.Response {
data: GetLoginQrData;
}
/**
* @description 获取登录二维码返回数据
* @since Beta v0.3.0
* @interface GetLoginQrData
* @property {string} ticket 二维码票据
* @property {string} url 二维码链接
* @return GetLoginQrData
*/
export interface GetLoginQrData {
ticket: string;
url: string;
}
/**
* @description 获取登录状态返回数据
* @since Beta v0.3.0
* @interface GetLoginStatusResponse
* @extends TGApp.Plugins.Mys.Base.Response
* @property {GetLoginStatusData} data 数据
* @return GetLoginStatusResponse
*/
export interface GetLoginStatusResponse extends TGApp.Plugins.Mys.Base.Response {
data: GetLoginStatusData;
}
/**
* @description 获取登录状态返回数据
* @since Beta v0.3.0
* @interface GetLoginStatusData
* @property {string} app_id 应用 ID
* @property {number} client_type 客户端类型
* @property {string} created_at 创建时间
* @property {boolean} need_realperson 是否需要人机验证
* @property {RealNameInfo} realname_info 实名信息
* @property {string} scanned_at 扫码时间
* @property {string} status 状态
* @property {unknown[]} tokens 令牌
* @property {UserInfo} user_info 用户信息
* @return GetLoginStatusData
*/
export interface GetLoginStatusData {
app_id: string;
client_type: number;
created_at: string;
need_realperson: boolean;
realname_info: RealNameInfo;
scanned_at: string;
status: string;
tokens: unknown[];
user_info: UserInfo;
}
/**
* @description 实名信息
* @since Beta v0.3.0
* @interface RealNameInfo
* @property {string} action_ticket 动作票据
* @property {string} action_type 动作类型
* @property {boolean} required 是否必须
* @return RealNameInfo
*/
interface RealNameInfo {
action_ticket: string;
action_type: string;
required: boolean;
}
/**
* @description 用户信息
* @since Beta v0.3.0
* @interface UserInfo
* @property {string} account_name 账户名
* @property {string} aid 账户 ID
* @property {string} area_code 区域代码
* @property {string} country 国家
* @property {string} email 邮箱 // 为加密后的邮箱包含 * 号
* @property {string} identity_code 身份证号 // 为加密后的身份证号包含 * 号
* @property {number} is_email_verify 是否邮箱验证 // 1 为已验证
* @property {unknown[]} links 链接
* @property {string} mid 用户 ID
* @property {string} mobile 手机号 // 为加密后的手机号包含 * 号
* @property {string} realname 真实姓名 // 为加密后的真实姓名包含 * 号
* @property {string} rebind_area_code 重新绑定区域代码
* @property {string} rebind_mobile 重新绑定手机号
* @property {string} rebind_mobile_time 重新绑定手机号时间
* @property {string} safe_area_code 安全区域代码
* @property {string} safe_mobile 安全手机号
* @return UserInfo
*/
interface UserInfo {
account_name: string;
aid: string;
area_code: string;
country: string;
email: string;
identity_code: string;
is_email_verify: number;
links: unknown[];
mid: string;
mobile: string;
realname: string;
rebind_area_code: string;
rebind_mobile: string;
rebind_mobile_time: string;
safe_area_code: string;
safe_mobile: string;
}
}

View File

@@ -0,0 +1,90 @@
/**
* @file plugins Mys utils getLoginQr
* @description 获取登录二维码
* @author BTMuli <bt-muli@outlook.com>
* @since Beta v0.3.0
*/
// tauri
import { http } from "@tauri-apps/api";
const uuid = crypto.randomUUID();
const headers: Record<string, string> = {
Accept: "application/json, text/plain, */*",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9",
Connection: "keep-alive",
Origin: "https://user.miyoushe.com",
Referer: "https://user.miyoushe.com/",
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
"x-rpc-app_id": "bll8iq97cem8",
"x-rpc-client_type": "4",
"x-rpc-device_model": "Chrome 116.0.0.0",
"x-rpc-device_name": "Chrome",
"x-rpc-device_os": "Windows 10 64-bit",
"x-rpc-game_biz": "bbs_cn",
"x-rpc-mi_referrer":
"https://user.miyoushe.com/login-platform/index.html?app_id=bll8iq97cem8&app_version=2.58.0&theme=&token_type=4&game_biz=bbs_cn&message_origin=https%253A%252F%252Fwww.miyoushe.com&succ_back_type=message%253Alogin-platform%253Alogin-success&fail_back_type=message%253Alogin-platform%253Alogin-fail&sync_login_status=popup&ux_mode=popup&iframe_level=1&ap_mps=1#/login/qr",
"x-rpc-sdk_version": "2.17.0",
"X-Rpc-Device_fp": "",
"X-Rpc-Device_id": uuid,
};
/**
* @description 获取登录二维码
* @since Beta v0.3.0
* @returns {Promise<TGApp.Plugins.Mys.WebLogin.GetLoginQrData|TGApp.Plugins.Mys.Base.Response>}
*/
export async function getLoginQr(): Promise<
TGApp.Plugins.Mys.WebLogin.GetLoginQrData | TGApp.Plugins.Mys.Base.Response
> {
const url = "https://passport-api.miyoushe.com/account/ma-cn-passport/web/createQRLogin";
return await http
.fetch<TGApp.Plugins.Mys.WebLogin.GetLoginQrResponse>(url, {
method: "POST",
headers,
body: http.Body.json({}),
})
.then((res) => {
if (res.data.retcode === 0) {
return res.data.data;
} else {
return res.data;
}
});
}
/**
* @description 获取登录状态
* @since Beta v0.3.0
* @param {string} ticket 二维码票据
* @returns {Promise<Record<string, string[]>}
*/
export async function getLoginStatus(
ticket: string,
): Promise<TGApp.Plugins.Mys.WebLogin.GetLoginStatusResponse | Record<string, string>> {
const url = "https://passport-api.miyoushe.com/account/ma-cn-passport/web/queryQRLoginStatus";
const resp = await http.fetch<TGApp.Plugins.Mys.WebLogin.GetLoginStatusResponse>(url, {
method: "POST",
headers,
body: http.Body.json({ ticket }),
});
const data = resp.data;
if (data.retcode !== 0) return resp.data;
if (data.data.status === "Created" || data.data.status === "Scanned") {
await new Promise((resolve) => setTimeout(resolve, 5000));
return await getLoginStatus(ticket);
}
if (data.data.status === "Confirmed") {
const cookieList = resp.rawHeaders["set-cookie"].map((cookie) => cookie.split(";")[0]);
const cookieRecord: Record<string, string> = {};
cookieList.forEach((cookie) => {
const [key, value] = cookie.split("=");
cookieRecord[key] = value;
});
return cookieRecord;
}
return resp.data;
}