feat: 添加阿里云盘扫码登录v2功能 - 新增安全的多用户并发登录支持,客户端指纹验证,会话管理和自动过期机制,优化UI体验
This commit is contained in:
parent
aa386512c2
commit
760a22bff7
@ -91,6 +91,121 @@
|
|||||||
[data-theme="dark"] p {
|
[data-theme="dark"] p {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 阿里云盘扫码v2样式 */
|
||||||
|
.qr-modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1000;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-modal-content {
|
||||||
|
background-color: #fefefe;
|
||||||
|
margin: 5% auto;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 500px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .qr-modal-content {
|
||||||
|
background-color: #2a2a3b;
|
||||||
|
color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code-container {
|
||||||
|
margin: 20px 0;
|
||||||
|
padding: 20px;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .qr-code-container {
|
||||||
|
background: #1a1a2e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code-img {
|
||||||
|
max-width: 200px;
|
||||||
|
max-height: 200px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-status {
|
||||||
|
margin: 15px 0;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-status.waiting {
|
||||||
|
background: #fff3cd;
|
||||||
|
color: #856404;
|
||||||
|
border: 1px solid #ffeaa7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-status.scaned {
|
||||||
|
background: #d1ecf1;
|
||||||
|
color: #0c5460;
|
||||||
|
border: 1px solid #bee5eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-status.success {
|
||||||
|
background: #d4edda;
|
||||||
|
color: #155724;
|
||||||
|
border: 1px solid #c3e6cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-status.error {
|
||||||
|
background: #f8d7da;
|
||||||
|
color: #721c24;
|
||||||
|
border: 1px solid #f5c6cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .qr-status.waiting {
|
||||||
|
background: #3d3d00;
|
||||||
|
color: #ffeb3b;
|
||||||
|
border: 1px solid #ffeb3b;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .qr-status.scaned {
|
||||||
|
background: #003d4d;
|
||||||
|
color: #00bcd4;
|
||||||
|
border: 1px solid #00bcd4;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .qr-status.success {
|
||||||
|
background: #1b5e20;
|
||||||
|
color: #4caf50;
|
||||||
|
border: 1px solid #4caf50;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .qr-status.error {
|
||||||
|
background: #5d1a1a;
|
||||||
|
color: #f44336;
|
||||||
|
border: 1px solid #f44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
color: #aaa;
|
||||||
|
float: right;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn:hover {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .close-btn:hover {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -108,6 +223,7 @@
|
|||||||
<option value="onedrive_us">OneDrive 美国版本</option>
|
<option value="onedrive_us">OneDrive 美国版本</option>
|
||||||
<option value="onedrive_de">OneDrive 德国版本</option>
|
<option value="onedrive_de">OneDrive 德国版本</option>
|
||||||
<option value="alicloud_qr">阿里网盘 扫码登录</option>
|
<option value="alicloud_qr">阿里网盘 扫码登录</option>
|
||||||
|
<option value="alicloud_qr2">阿里云盘 扫码登录v2</option>
|
||||||
<option value="baiduyun_go">百度网盘 验证登录</option>
|
<option value="baiduyun_go">百度网盘 验证登录</option>
|
||||||
<option value="115cloud_go">115 网盘 验证登录</option>
|
<option value="115cloud_go">115 网盘 验证登录</option>
|
||||||
<option value="123cloud_go">123 网盘 直接登录</option>
|
<option value="123cloud_go">123 网盘 直接登录</option>
|
||||||
@ -165,6 +281,22 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 阿里云盘扫码v2模态框 -->
|
||||||
|
<div id="qr-modal" class="qr-modal">
|
||||||
|
<div class="qr-modal-content">
|
||||||
|
<span class="close-btn" onclick="closeQRModal()">×</span>
|
||||||
|
<h4>阿里云盘扫码登录v2</h4>
|
||||||
|
<div id="qr-code-container" class="qr-code-container" style="display: none;">
|
||||||
|
<div id="qr-code-display"></div>
|
||||||
|
</div>
|
||||||
|
<div id="qr-status" class="qr-status" style="display: none;"></div>
|
||||||
|
<div class="mt-3">
|
||||||
|
<button id="refresh-qr-btn" class="btn btn-secondary" onclick="refreshQRCode()" style="display: none;">刷新二维码</button>
|
||||||
|
<button class="btn btn-secondary" onclick="closeQRModal()">关闭</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -178,7 +310,8 @@
|
|||||||
let apps_type = document.getElementById("site-select").value;
|
let apps_type = document.getElementById("site-select").value;
|
||||||
let secret_key = document.getElementById("secret-key").value;
|
let secret_key = document.getElementById("secret-key").value;
|
||||||
console.log(server_use);
|
console.log(server_use);
|
||||||
if (!server_use && (apps_uuid === "" || apps_keys === "")) {
|
// 阿里云盘扫码登录v2不需要验证客户端ID和应用机密
|
||||||
|
if (apps_type !== "alicloud_qr2" && !server_use && (apps_uuid === "" || apps_keys === "")) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
position: 'top',
|
position: 'top',
|
||||||
icon: 'info',
|
icon: 'info',
|
||||||
@ -188,6 +321,12 @@
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 阿里云盘扫码v2直接调用专用API,不需要构建传统的requests路径
|
||||||
|
if (apps_type === "alicloud_qr2") {
|
||||||
|
await startAlicloud2Login();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let apps_subs = apps_type.split("_")[0]
|
let apps_subs = apps_type.split("_")[0]
|
||||||
let post_urls = "/" + apps_subs + "/requests?client_uid=" + apps_uuid
|
let post_urls = "/" + apps_subs + "/requests?client_uid=" + apps_uuid
|
||||||
+ "&client_key=" + apps_keys + "&apps_types=" + apps_type
|
+ "&client_key=" + apps_keys + "&apps_types=" + apps_type
|
||||||
@ -232,6 +371,7 @@
|
|||||||
+ `&client_key=${apps_keys}`;
|
+ `&client_key=${apps_keys}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else Swal.fire({
|
} else Swal.fire({
|
||||||
icon: 'error',
|
icon: 'error',
|
||||||
title: "获取秘钥失败: " + response_data.text,
|
title: "获取秘钥失败: " + response_data.text,
|
||||||
@ -303,6 +443,44 @@
|
|||||||
// 更新输入框的值
|
// 更新输入框的值
|
||||||
callbackUrlInput.value = `https://api.oplist.org/${selectedValue}/callback`;
|
callbackUrlInput.value = `https://api.oplist.org/${selectedValue}/callback`;
|
||||||
document.getElementById('secret-key-view').hidden = true;
|
document.getElementById('secret-key-view').hidden = true;
|
||||||
|
|
||||||
|
const clientIdInput = document.getElementById('client-id');
|
||||||
|
const appSecretInput = document.getElementById('app-secret');
|
||||||
|
const serverUseCheckbox = document.getElementById('server_use');
|
||||||
|
const clientIdContainer = clientIdInput.closest('.mb-3');
|
||||||
|
const appSecretContainer = appSecretInput.closest('.mb-3');
|
||||||
|
const serverUseContainer = serverUseCheckbox.closest('.mb-3');
|
||||||
|
const callbackContainer = callbackUrlInput.closest('.mb-3');
|
||||||
|
|
||||||
|
// 阿里云盘扫码登录v2不需要客户端ID、应用机密和回调地址
|
||||||
|
if (siteSelect.value === "alicloud_qr2") {
|
||||||
|
// 隐藏整个字段容器
|
||||||
|
clientIdContainer.style.display = 'none';
|
||||||
|
appSecretContainer.style.display = 'none';
|
||||||
|
serverUseContainer.style.display = 'none';
|
||||||
|
callbackContainer.style.display = 'none';
|
||||||
|
|
||||||
|
// 清空值
|
||||||
|
clientIdInput.value = '';
|
||||||
|
appSecretInput.value = '';
|
||||||
|
serverUseCheckbox.checked = false;
|
||||||
|
} else {
|
||||||
|
// 恢复显示
|
||||||
|
clientIdContainer.style.display = 'block';
|
||||||
|
appSecretContainer.style.display = 'block';
|
||||||
|
serverUseContainer.style.display = 'block';
|
||||||
|
callbackContainer.style.display = 'block';
|
||||||
|
|
||||||
|
// 恢复正常状态
|
||||||
|
if (!serverUseCheckbox.checked) {
|
||||||
|
clientIdInput.disabled = false;
|
||||||
|
appSecretInput.disabled = false;
|
||||||
|
}
|
||||||
|
clientIdInput.placeholder = '';
|
||||||
|
appSecretInput.placeholder = '';
|
||||||
|
serverUseCheckbox.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (siteSelect.value === "baiduyun_go") {
|
if (siteSelect.value === "baiduyun_go") {
|
||||||
document.getElementById('secret-key-view').hidden = false;
|
document.getElementById('secret-key-view').hidden = false;
|
||||||
}
|
}
|
||||||
@ -314,6 +492,7 @@
|
|||||||
const secretKeyInput = document.getElementById('secret-key');
|
const secretKeyInput = document.getElementById('secret-key');
|
||||||
const server_flag = document.getElementById('server_use');
|
const server_flag = document.getElementById('server_use');
|
||||||
if ((siteSelect.value === "alicloud_qr"
|
if ((siteSelect.value === "alicloud_qr"
|
||||||
|
|| siteSelect.value === "alicloud_qr2"
|
||||||
|| siteSelect.value === "123cloud_go"
|
|| siteSelect.value === "123cloud_go"
|
||||||
|| siteSelect.value === "onedrive_cn"
|
|| siteSelect.value === "onedrive_cn"
|
||||||
|| siteSelect.value === "onedrive_us"
|
|| siteSelect.value === "onedrive_us"
|
||||||
@ -376,6 +555,269 @@
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
getToken();
|
getToken();
|
||||||
|
|
||||||
|
// 阿里云盘扫码v2相关变量
|
||||||
|
let alicloud2SessionId = null;
|
||||||
|
let alicloud2CheckInterval = null;
|
||||||
|
let alicloud2StartTime = null;
|
||||||
|
let clientFingerprint = null;
|
||||||
|
|
||||||
|
// 生成客户端指纹
|
||||||
|
function generateClientFingerprint() {
|
||||||
|
if (clientFingerprint) return clientFingerprint;
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.textBaseline = 'top';
|
||||||
|
ctx.font = '14px Arial';
|
||||||
|
ctx.fillText('Client fingerprint', 2, 2);
|
||||||
|
|
||||||
|
const fingerprint = [
|
||||||
|
navigator.userAgent,
|
||||||
|
navigator.language,
|
||||||
|
screen.width + 'x' + screen.height,
|
||||||
|
new Date().getTimezoneOffset(),
|
||||||
|
canvas.toDataURL(),
|
||||||
|
navigator.hardwareConcurrency || 'unknown',
|
||||||
|
navigator.deviceMemory || 'unknown'
|
||||||
|
].join('|');
|
||||||
|
|
||||||
|
// 生成简单的哈希
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < fingerprint.length; i++) {
|
||||||
|
const char = fingerprint.charCodeAt(i);
|
||||||
|
hash = ((hash << 5) - hash) + char;
|
||||||
|
hash = hash & hash; // 转换为32位整数
|
||||||
|
}
|
||||||
|
|
||||||
|
clientFingerprint = Math.abs(hash).toString(36);
|
||||||
|
console.log('客户端指纹生成:', clientFingerprint);
|
||||||
|
return clientFingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送带有客户端指纹的请求
|
||||||
|
async function fetchWithFingerprint(url, options = {}) {
|
||||||
|
const fingerprint = generateClientFingerprint();
|
||||||
|
const headers = {
|
||||||
|
'X-Client-Fingerprint': fingerprint,
|
||||||
|
...options.headers
|
||||||
|
};
|
||||||
|
|
||||||
|
return fetch(url, {
|
||||||
|
...options,
|
||||||
|
headers
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动阿里云盘扫码v2登录
|
||||||
|
async function startAlicloud2Login() {
|
||||||
|
try {
|
||||||
|
// 显示模态框
|
||||||
|
document.getElementById('qr-modal').style.display = 'block';
|
||||||
|
setQRStatus('正在生成二维码...', 'waiting');
|
||||||
|
|
||||||
|
// 生成二维码 - 使用带指纹的请求
|
||||||
|
const response = await fetchWithFingerprint('/alicloud2/generate_qr');
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
alicloud2SessionId = result.session_id;
|
||||||
|
alicloud2StartTime = Date.now();
|
||||||
|
showQRCode(result.qr_code_url);
|
||||||
|
setQRStatus('请使用阿里云盘App扫描二维码', 'waiting');
|
||||||
|
|
||||||
|
// 显示过期时间信息
|
||||||
|
if (result.expires_in) {
|
||||||
|
const expireMinutes = Math.floor(result.expires_in / 60);
|
||||||
|
console.log(`会话将在 ${expireMinutes} 分钟后过期`);
|
||||||
|
}
|
||||||
|
|
||||||
|
startStatusCheck();
|
||||||
|
} else {
|
||||||
|
setQRStatus(result.error || '生成二维码失败', 'error');
|
||||||
|
document.getElementById('refresh-qr-btn').style.display = 'inline-block';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setQRStatus('网络错误,请重试', 'error');
|
||||||
|
document.getElementById('refresh-qr-btn').style.display = 'inline-block';
|
||||||
|
console.error('生成二维码失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示二维码
|
||||||
|
function showQRCode(qrUrl) {
|
||||||
|
const qrApiUrl = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(qrUrl)}`;
|
||||||
|
document.getElementById('qr-code-display').innerHTML = `<img src="${qrApiUrl}" alt="二维码" class="qr-code-img">`;
|
||||||
|
document.getElementById('qr-code-container').style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置状态
|
||||||
|
function setQRStatus(message, type) {
|
||||||
|
const statusEl = document.getElementById('qr-status');
|
||||||
|
statusEl.textContent = message;
|
||||||
|
statusEl.className = `qr-status ${type}`;
|
||||||
|
statusEl.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始状态检查
|
||||||
|
function startStatusCheck() {
|
||||||
|
stopStatusCheck();
|
||||||
|
alicloud2CheckInterval = setInterval(checkAlicloud2Status, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止状态检查
|
||||||
|
function stopStatusCheck() {
|
||||||
|
if (alicloud2CheckInterval) {
|
||||||
|
clearInterval(alicloud2CheckInterval);
|
||||||
|
alicloud2CheckInterval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查登录状态
|
||||||
|
async function checkAlicloud2Status() {
|
||||||
|
if (!alicloud2SessionId) return;
|
||||||
|
|
||||||
|
// 检查是否超过3分钟(二维码可能过期)
|
||||||
|
const elapsed = Date.now() - alicloud2StartTime;
|
||||||
|
if (elapsed > 180000) { // 3分钟
|
||||||
|
setQRStatus('二维码可能已过期,建议点击刷新重新生成', 'error');
|
||||||
|
document.getElementById('refresh-qr-btn').style.display = 'inline-block';
|
||||||
|
stopStatusCheck();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用带指纹的请求
|
||||||
|
const response = await fetchWithFingerprint(`/alicloud2/check_login?session_id=${alicloud2SessionId}`);
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
switch (result.status) {
|
||||||
|
case 'WAITING':
|
||||||
|
const waitTime = Math.floor(elapsed / 1000);
|
||||||
|
setQRStatus(`等待扫描... (${waitTime}s) 请使用阿里云盘App扫码`, 'waiting');
|
||||||
|
break;
|
||||||
|
case 'SCANED':
|
||||||
|
setQRStatus('已扫描,请在手机上确认登录', 'scaned');
|
||||||
|
break;
|
||||||
|
case 'CONFIRMED':
|
||||||
|
setQRStatus('登录成功!正在获取用户信息...', 'success');
|
||||||
|
stopStatusCheck();
|
||||||
|
// 稍等一下确保token已保存
|
||||||
|
setTimeout(async () => {
|
||||||
|
await getAlicloud2UserInfo();
|
||||||
|
}, 1000);
|
||||||
|
break;
|
||||||
|
case 'EXPIRED':
|
||||||
|
setQRStatus('二维码已过期,请点击刷新重新生成', 'error');
|
||||||
|
stopStatusCheck();
|
||||||
|
document.getElementById('refresh-qr-btn').style.display = 'inline-block';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 处理会话验证失败的情况
|
||||||
|
if (response.status === 403) {
|
||||||
|
setQRStatus('会话验证失败,请重新生成二维码', 'error');
|
||||||
|
document.getElementById('refresh-qr-btn').style.display = 'inline-block';
|
||||||
|
stopStatusCheck();
|
||||||
|
} else {
|
||||||
|
setQRStatus('检查状态失败: ' + (result.error || '未知错误'), 'error');
|
||||||
|
document.getElementById('refresh-qr-btn').style.display = 'inline-block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('检查登录状态失败:', error);
|
||||||
|
setQRStatus('网络连接失败,请检查网络后重试', 'error');
|
||||||
|
document.getElementById('refresh-qr-btn').style.display = 'inline-block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
async function getAlicloud2UserInfo() {
|
||||||
|
if (!alicloud2SessionId) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用带指纹的请求
|
||||||
|
const response = await fetchWithFingerprint(`/alicloud2/get_user_info?session_id=${alicloud2SessionId}`);
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success && result.user_info) {
|
||||||
|
// 关闭模态框
|
||||||
|
closeQRModal();
|
||||||
|
|
||||||
|
// 显示成功消息
|
||||||
|
await Swal.fire({
|
||||||
|
position: 'top',
|
||||||
|
icon: 'success',
|
||||||
|
title: '登录成功',
|
||||||
|
html: `<div>用户: ${result.user_info.nick_name || result.user_info.user_id}</div>`,
|
||||||
|
showConfirmButton: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 填充token字段(使用真实的tokens)
|
||||||
|
if (result.access_token) {
|
||||||
|
document.getElementById("access-token").value = result.access_token;
|
||||||
|
}
|
||||||
|
if (result.refresh_token) {
|
||||||
|
document.getElementById("refresh-token").value = result.refresh_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理会话
|
||||||
|
await fetchWithFingerprint(`/alicloud2/logout?session_id=${alicloud2SessionId}`);
|
||||||
|
alicloud2SessionId = null;
|
||||||
|
} else {
|
||||||
|
// 处理会话验证失败的情况
|
||||||
|
if (response.status === 403) {
|
||||||
|
setQRStatus('会话验证失败,请重新登录', 'error');
|
||||||
|
} else {
|
||||||
|
setQRStatus('获取用户信息失败: ' + (result.error || '未知错误'), 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setQRStatus('获取用户信息失败', 'error');
|
||||||
|
console.error('获取用户信息失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新二维码
|
||||||
|
async function refreshQRCode() {
|
||||||
|
document.getElementById('refresh-qr-btn').style.display = 'none';
|
||||||
|
// 清理旧会话
|
||||||
|
if (alicloud2SessionId) {
|
||||||
|
try {
|
||||||
|
await fetchWithFingerprint(`/alicloud2/logout?session_id=${alicloud2SessionId}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log('清理旧会话失败:', e);
|
||||||
|
}
|
||||||
|
alicloud2SessionId = null;
|
||||||
|
}
|
||||||
|
await startAlicloud2Login();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭模态框
|
||||||
|
function closeQRModal() {
|
||||||
|
document.getElementById('qr-modal').style.display = 'none';
|
||||||
|
stopStatusCheck();
|
||||||
|
|
||||||
|
// 清理会话
|
||||||
|
if (alicloud2SessionId) {
|
||||||
|
fetchWithFingerprint(`/alicloud2/logout?session_id=${alicloud2SessionId}`);
|
||||||
|
alicloud2SessionId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置界面
|
||||||
|
document.getElementById('qr-code-container').style.display = 'none';
|
||||||
|
document.getElementById('qr-status').style.display = 'none';
|
||||||
|
document.getElementById('refresh-qr-btn').style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击模态框外部关闭
|
||||||
|
window.onclick = function(event) {
|
||||||
|
const modal = document.getElementById('qr-modal');
|
||||||
|
if (event.target === modal) {
|
||||||
|
closeQRModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
692
src/aliui2.ts
Normal file
692
src/aliui2.ts
Normal file
@ -0,0 +1,692 @@
|
|||||||
|
import {Context} from "hono";
|
||||||
|
import * as local from "hono/cookie";
|
||||||
|
|
||||||
|
// 阿里云盘扫码登录相关接口定义
|
||||||
|
interface QRCodeData {
|
||||||
|
qrCodeUrl: string;
|
||||||
|
ck?: string;
|
||||||
|
t?: string;
|
||||||
|
resultCode?: number;
|
||||||
|
processFinished?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface QRStatusResponse {
|
||||||
|
success: boolean;
|
||||||
|
content: {
|
||||||
|
qrCodeStatus: string;
|
||||||
|
resultCode: number;
|
||||||
|
bizExt?: any;
|
||||||
|
data?: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserInfo {
|
||||||
|
user_id: string;
|
||||||
|
nick_name: string;
|
||||||
|
avatar: string;
|
||||||
|
phone: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DriveInfo {
|
||||||
|
total_size: number;
|
||||||
|
used_size: number;
|
||||||
|
album_drive_used_size: number;
|
||||||
|
note_drive_used_size: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 阿里云盘API响应接口
|
||||||
|
interface AliCloudApiResponse {
|
||||||
|
hasError?: boolean;
|
||||||
|
content?: {
|
||||||
|
success?: boolean;
|
||||||
|
data?: {
|
||||||
|
codeContent?: string;
|
||||||
|
ck?: string;
|
||||||
|
t?: string;
|
||||||
|
resultCode?: number;
|
||||||
|
processFinished?: boolean;
|
||||||
|
qrCodeStatus?: string;
|
||||||
|
bizExt?: any;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 阿里云盘扫码登录类
|
||||||
|
class AlipanQRLogin {
|
||||||
|
private session_id: string;
|
||||||
|
private csrf_token: string;
|
||||||
|
private umid_token: string;
|
||||||
|
private qr_code_data: QRCodeData | null = null;
|
||||||
|
private access_token: string | null = null;
|
||||||
|
private refresh_token: string | null = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.session_id = this.generateUUID();
|
||||||
|
this.csrf_token = "MuSysYVxW5AMGblcOTSKb3";
|
||||||
|
this.umid_token = this.generateUUID().replace(/-/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateUUID(): string {
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||||
|
const r = Math.random() * 16 | 0;
|
||||||
|
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getHeaders(): Record<string, string> {
|
||||||
|
return {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||||
|
'Accept': 'application/json, text/plain, */*',
|
||||||
|
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||||
|
'Accept-Encoding': 'gzip, deflate, br',
|
||||||
|
'Connection': 'keep-alive',
|
||||||
|
'Sec-Fetch-Dest': 'empty',
|
||||||
|
'Sec-Fetch-Mode': 'cors',
|
||||||
|
'Sec-Fetch-Site': 'same-site',
|
||||||
|
'Referer': 'https://passport.alipan.com/',
|
||||||
|
'Origin': 'https://passport.alipan.com'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取OAuth认证URL
|
||||||
|
async getOAuthUrl(): Promise<string | null> {
|
||||||
|
try {
|
||||||
|
const authUrl = "https://auth.alipan.com/v2/oauth/authorize";
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
'client_id': '25dzX3vbYqktVxyX',
|
||||||
|
'redirect_uri': 'https://www.alipan.com/sign/callback',
|
||||||
|
'response_type': 'code',
|
||||||
|
'login_type': 'custom',
|
||||||
|
'state': '{"origin":"https://www.alipan.com"}'
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await fetch(`${authUrl}?${params}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: this.getHeaders()
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
return response.url;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取OAuth URL失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取登录页面信息
|
||||||
|
async getLoginPage(): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const loginUrl = "https://passport.alipan.com/mini_login.htm";
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
'lang': 'zh_cn',
|
||||||
|
'appName': 'aliyun_drive',
|
||||||
|
'appEntrance': 'web_default',
|
||||||
|
'styleType': 'auto',
|
||||||
|
'bizParams': '',
|
||||||
|
'notLoadSsoView': 'false',
|
||||||
|
'notKeepLogin': 'false',
|
||||||
|
'isMobile': 'false',
|
||||||
|
'ad__pass__q__rememberLogin': 'true',
|
||||||
|
'ad__pass__q__rememberLoginDefaultValue': 'true',
|
||||||
|
'ad__pass__q__forgotPassword': 'true',
|
||||||
|
'ad__pass__q__licenseMargin': 'true',
|
||||||
|
'ad__pass__q__loginType': 'normal',
|
||||||
|
'hidePhoneCode': 'true',
|
||||||
|
'rnd': Date.now().toString()
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await fetch(`${loginUrl}?${params}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: this.getHeaders()
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const content = await response.text();
|
||||||
|
|
||||||
|
// 尝试提取CSRF token
|
||||||
|
const csrfMatch = content.match(/_csrf_token["']?\s*[:=]\s*["']([^"']+)["']/);
|
||||||
|
if (csrfMatch) {
|
||||||
|
this.csrf_token = csrfMatch[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试提取umidToken
|
||||||
|
const umidMatch = content.match(/umidToken["']?\s*[:=]\s*["']([^"']+)["']/);
|
||||||
|
if (umidMatch) {
|
||||||
|
this.umid_token = umidMatch[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取登录页面失败:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成二维码
|
||||||
|
async generateQRCode(): Promise<QRCodeData | null> {
|
||||||
|
try {
|
||||||
|
const qrUrl = "https://passport.alipan.com/newlogin/qrcode/generate.do";
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
'appName': 'aliyun_drive',
|
||||||
|
'fromSite': '52',
|
||||||
|
'appEntrance': 'web_default',
|
||||||
|
'_csrf_token': this.csrf_token,
|
||||||
|
'umidToken': this.umid_token,
|
||||||
|
'hsiz': '115d9f5f2cf2f87850a93a793aaaecb4',
|
||||||
|
'bizParams': 'taobaoBizLoginFrom=web_default&renderRefer=https%3A%2F%2Fauth.alipan.com%2F',
|
||||||
|
'mainPage': 'false',
|
||||||
|
'isMobile': 'false',
|
||||||
|
'lang': 'zh_CN',
|
||||||
|
'returnUrl': '',
|
||||||
|
'umidTag': 'SERVER'
|
||||||
|
});
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
...this.getHeaders(),
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(`${qrUrl}?${params}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: headers
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const result = await response.json() as AliCloudApiResponse;
|
||||||
|
|
||||||
|
if (!result.hasError) {
|
||||||
|
const content = result.content || {};
|
||||||
|
if (content.success) {
|
||||||
|
const data = content.data || {};
|
||||||
|
const codeContent = data.codeContent;
|
||||||
|
|
||||||
|
if (codeContent) {
|
||||||
|
this.qr_code_data = {
|
||||||
|
qrCodeUrl: codeContent,
|
||||||
|
ck: data.ck,
|
||||||
|
t: data.t,
|
||||||
|
resultCode: data.resultCode,
|
||||||
|
processFinished: data.processFinished
|
||||||
|
};
|
||||||
|
return this.qr_code_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('生成二维码失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询二维码状态
|
||||||
|
async queryQRStatus(): Promise<QRStatusResponse | null> {
|
||||||
|
try {
|
||||||
|
if (!this.qr_code_data) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryUrl = "https://passport.alipan.com/newlogin/qrcode/query.do";
|
||||||
|
const formData = new URLSearchParams({
|
||||||
|
'appName': 'aliyun_drive',
|
||||||
|
'fromSite': '52'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.qr_code_data.ck) {
|
||||||
|
formData.append('ck', this.qr_code_data.ck);
|
||||||
|
}
|
||||||
|
if (this.qr_code_data.t) {
|
||||||
|
formData.append('t', this.qr_code_data.t);
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
...this.getHeaders(),
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||||
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(queryUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: headers,
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const result = await response.json() as AliCloudApiResponse;
|
||||||
|
|
||||||
|
if (!result.hasError) {
|
||||||
|
const content = result.content || {};
|
||||||
|
if (content.success) {
|
||||||
|
const data = content.data || {};
|
||||||
|
const apiQrStatus = data.qrCodeStatus || 'NEW';
|
||||||
|
const resultCode = data.resultCode || 0;
|
||||||
|
|
||||||
|
// 状态映射
|
||||||
|
const statusMapping: Record<string, string> = {
|
||||||
|
'NEW': 'WAITING',
|
||||||
|
'SCANED': 'SCANED',
|
||||||
|
'CONFIRMED': 'CONFIRMED',
|
||||||
|
'EXPIRED': 'EXPIRED'
|
||||||
|
};
|
||||||
|
|
||||||
|
const qrCodeStatus = statusMapping[apiQrStatus] || 'WAITING';
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
content: {
|
||||||
|
qrCodeStatus: qrCodeStatus,
|
||||||
|
resultCode: resultCode,
|
||||||
|
bizExt: data.bizExt || {},
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('查询二维码状态失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取访问令牌
|
||||||
|
async getAccessToken(bizExt: any): Promise<string | null> {
|
||||||
|
try {
|
||||||
|
console.log('getAccessToken - bizExt type:', typeof bizExt);
|
||||||
|
console.log('getAccessToken - bizExt:', bizExt);
|
||||||
|
|
||||||
|
// bizExt 是 Base64 编码的字符串,需要先解码
|
||||||
|
let decodedBizExt: any;
|
||||||
|
if (typeof bizExt === 'string') {
|
||||||
|
try {
|
||||||
|
const decodedString = atob(bizExt);
|
||||||
|
decodedBizExt = JSON.parse(decodedString);
|
||||||
|
console.log('getAccessToken - decoded bizExt:', JSON.stringify(decodedBizExt, null, 2));
|
||||||
|
} catch (decodeError) {
|
||||||
|
console.error('解码 bizExt 失败:', decodeError);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
decodedBizExt = bizExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decodedBizExt || !decodedBizExt.pds_login_result) {
|
||||||
|
console.log('getAccessToken - No pds_login_result found in decoded data');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const loginResult = decodedBizExt.pds_login_result;
|
||||||
|
console.log('getAccessToken - loginResult:', JSON.stringify(loginResult, null, 2));
|
||||||
|
this.access_token = loginResult.accessToken;
|
||||||
|
this.refresh_token = loginResult.refreshToken;
|
||||||
|
console.log('getAccessToken - access_token set:', this.access_token ? 'success' : 'failed');
|
||||||
|
console.log('getAccessToken - refresh_token set:', this.refresh_token ? 'success' : 'failed');
|
||||||
|
return this.access_token;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取访问令牌失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
async getUserInfo(): Promise<UserInfo | null> {
|
||||||
|
try {
|
||||||
|
if (!this.access_token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userUrl = "https://user.aliyundrive.com/v2/user/get";
|
||||||
|
const headers = {
|
||||||
|
'Authorization': `Bearer ${this.access_token}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(userUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: headers,
|
||||||
|
body: JSON.stringify({})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
return await response.json() as UserInfo;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取用户信息失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取网盘信息
|
||||||
|
async getDriveInfo(): Promise<DriveInfo | null> {
|
||||||
|
try {
|
||||||
|
if (!this.access_token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const driveUrl = "https://api.aliyundrive.com/adrive/v1/user/driveCapacityDetails";
|
||||||
|
const headers = {
|
||||||
|
'Authorization': `Bearer ${this.access_token}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(driveUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: headers,
|
||||||
|
body: JSON.stringify({})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
return await response.json() as DriveInfo;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取网盘信息失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否已登录
|
||||||
|
isLoggedIn(): boolean {
|
||||||
|
return !!this.access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取访问令牌(用于返回给前端)
|
||||||
|
getToken(): string | null {
|
||||||
|
return this.access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取刷新令牌
|
||||||
|
getRefreshToken(): string | null {
|
||||||
|
return this.refresh_token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 会话管理接口
|
||||||
|
interface SessionData {
|
||||||
|
instance: AlipanQRLogin;
|
||||||
|
createdAt: number;
|
||||||
|
lastAccess: number;
|
||||||
|
clientFingerprint?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全局实例存储 - 改为存储会话数据而不是直接存储实例
|
||||||
|
const loginSessions = new Map<string, SessionData>();
|
||||||
|
|
||||||
|
// 会话过期时间(30分钟)
|
||||||
|
const SESSION_TIMEOUT = 30 * 60 * 1000;
|
||||||
|
|
||||||
|
// 生成安全的会话ID
|
||||||
|
function generateSecureSessionId(): string {
|
||||||
|
const timestamp = Date.now().toString(36);
|
||||||
|
const randomPart = Math.random().toString(36).substring(2, 15);
|
||||||
|
const randomPart2 = Math.random().toString(36).substring(2, 15);
|
||||||
|
return `${timestamp}-${randomPart}-${randomPart2}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理过期会话
|
||||||
|
function cleanupExpiredSessions() {
|
||||||
|
const now = Date.now();
|
||||||
|
for (const [sessionId, sessionData] of loginSessions.entries()) {
|
||||||
|
if (now - sessionData.lastAccess > SESSION_TIMEOUT) {
|
||||||
|
loginSessions.delete(sessionId);
|
||||||
|
console.log(`清理过期会话: ${sessionId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注意:不能在全局作用域使用 setInterval,改为在每次请求时检查过期会话
|
||||||
|
|
||||||
|
// 获取或创建会话
|
||||||
|
function getOrCreateSession(sessionId?: string, clientFingerprint?: string): { sessionId: string, sessionData: SessionData } {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
// 如果提供了sessionId,尝试获取现有会话
|
||||||
|
if (sessionId && loginSessions.has(sessionId)) {
|
||||||
|
const sessionData = loginSessions.get(sessionId)!;
|
||||||
|
|
||||||
|
// 检查会话是否过期
|
||||||
|
if (now - sessionData.lastAccess > SESSION_TIMEOUT) {
|
||||||
|
loginSessions.delete(sessionId);
|
||||||
|
console.log(`会话已过期,删除: ${sessionId}`);
|
||||||
|
} else {
|
||||||
|
// 更新最后访问时间
|
||||||
|
sessionData.lastAccess = now;
|
||||||
|
return { sessionId, sessionData };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新会话
|
||||||
|
const newSessionId = generateSecureSessionId();
|
||||||
|
const newSessionData: SessionData = {
|
||||||
|
instance: new AlipanQRLogin(),
|
||||||
|
createdAt: now,
|
||||||
|
lastAccess: now,
|
||||||
|
clientFingerprint
|
||||||
|
};
|
||||||
|
|
||||||
|
loginSessions.set(newSessionId, newSessionData);
|
||||||
|
console.log(`创建新会话: ${newSessionId}, 客户端指纹: ${clientFingerprint || 'none'}`);
|
||||||
|
|
||||||
|
return { sessionId: newSessionId, sessionData: newSessionData };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证会话所有权(可选的额外安全措施)
|
||||||
|
function validateSessionOwnership(sessionId: string, clientFingerprint?: string): boolean {
|
||||||
|
const sessionData = loginSessions.get(sessionId);
|
||||||
|
if (!sessionData) return false;
|
||||||
|
|
||||||
|
// 如果设置了客户端指纹,进行验证
|
||||||
|
if (sessionData.clientFingerprint && clientFingerprint) {
|
||||||
|
return sessionData.clientFingerprint === clientFingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成二维码接口
|
||||||
|
export async function generateQR(c: Context) {
|
||||||
|
try {
|
||||||
|
// 清理过期会话
|
||||||
|
cleanupExpiredSessions();
|
||||||
|
|
||||||
|
const requestedSessionId = c.req.query('session_id');
|
||||||
|
const clientFingerprint = c.req.header('X-Client-Fingerprint') || c.req.header('User-Agent');
|
||||||
|
|
||||||
|
// 获取或创建会话
|
||||||
|
const { sessionId, sessionData } = getOrCreateSession(requestedSessionId, clientFingerprint);
|
||||||
|
const alipan = sessionData.instance;
|
||||||
|
|
||||||
|
// 获取OAuth URL
|
||||||
|
const oauthUrl = await alipan.getOAuthUrl();
|
||||||
|
if (!oauthUrl) {
|
||||||
|
return c.json({error: '获取OAuth URL失败,请检查网络连接'}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取登录页面
|
||||||
|
const loginPageResult = await alipan.getLoginPage();
|
||||||
|
if (!loginPageResult) {
|
||||||
|
return c.json({error: '获取登录页面失败,请检查网络连接'}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成二维码
|
||||||
|
const qrData = await alipan.generateQRCode();
|
||||||
|
if (!qrData) {
|
||||||
|
return c.json({error: '生成二维码失败,可能是网络问题或API变化,请稍后重试'}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`会话 ${sessionId} 生成二维码成功`);
|
||||||
|
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
session_id: sessionId,
|
||||||
|
qr_code_url: qrData.qrCodeUrl,
|
||||||
|
message: '二维码生成成功,请使用阿里云盘App扫码登录',
|
||||||
|
expires_in: SESSION_TIMEOUT / 1000 // 返回过期时间(秒)
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('生成二维码失败:', error);
|
||||||
|
return c.json({error: '生成二维码失败'}, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查登录状态接口
|
||||||
|
export async function checkLogin(c: Context) {
|
||||||
|
try {
|
||||||
|
const sessionId = c.req.query('session_id');
|
||||||
|
if (!sessionId) {
|
||||||
|
return c.json({error: '缺少session_id参数'}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientFingerprint = c.req.header('X-Client-Fingerprint') || c.req.header('User-Agent');
|
||||||
|
|
||||||
|
// 验证会话所有权
|
||||||
|
if (!validateSessionOwnership(sessionId, clientFingerprint)) {
|
||||||
|
return c.json({error: '会话验证失败'}, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionData = loginSessions.get(sessionId);
|
||||||
|
if (!sessionData) {
|
||||||
|
return c.json({error: '会话不存在或已过期'}, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新最后访问时间
|
||||||
|
sessionData.lastAccess = Date.now();
|
||||||
|
|
||||||
|
const alipan = sessionData.instance;
|
||||||
|
|
||||||
|
// 查询二维码状态
|
||||||
|
const statusResult = await alipan.queryQRStatus();
|
||||||
|
if (!statusResult) {
|
||||||
|
return c.json({error: '查询登录状态失败'}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = statusResult.content.qrCodeStatus;
|
||||||
|
|
||||||
|
// 如果登录成功,获取访问令牌
|
||||||
|
if (status === 'CONFIRMED') {
|
||||||
|
const accessToken = await alipan.getAccessToken(statusResult.content.bizExt);
|
||||||
|
console.log(`会话 ${sessionId} - 登录确认,token获取: ${accessToken ? '成功' : '失败'}`);
|
||||||
|
if (accessToken) {
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
status: 'CONFIRMED',
|
||||||
|
message: '登录成功',
|
||||||
|
access_token: accessToken
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态消息映射
|
||||||
|
const statusMessages: Record<string, string> = {
|
||||||
|
'WAITING': '等待扫描',
|
||||||
|
'SCANED': '已扫描,等待确认',
|
||||||
|
'CONFIRMED': '登录成功',
|
||||||
|
'EXPIRED': '二维码已过期'
|
||||||
|
};
|
||||||
|
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
status: status,
|
||||||
|
message: statusMessages[status] || '未知状态'
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('检查登录状态失败:', error);
|
||||||
|
return c.json({error: '检查登录状态失败'}, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户信息接口
|
||||||
|
export async function getUserInfo(c: Context) {
|
||||||
|
try {
|
||||||
|
const sessionId = c.req.query('session_id');
|
||||||
|
if (!sessionId) {
|
||||||
|
return c.json({error: '缺少session_id参数'}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientFingerprint = c.req.header('X-Client-Fingerprint') || c.req.header('User-Agent');
|
||||||
|
|
||||||
|
// 验证会话所有权
|
||||||
|
if (!validateSessionOwnership(sessionId, clientFingerprint)) {
|
||||||
|
return c.json({error: '会话验证失败'}, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionData = loginSessions.get(sessionId);
|
||||||
|
if (!sessionData) {
|
||||||
|
return c.json({error: '会话不存在或已过期'}, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新最后访问时间
|
||||||
|
sessionData.lastAccess = Date.now();
|
||||||
|
|
||||||
|
const alipan = sessionData.instance;
|
||||||
|
|
||||||
|
// 检查是否已经登录成功
|
||||||
|
console.log(`会话 ${sessionId} - 登录状态: ${alipan.isLoggedIn()}, token: ${alipan.getToken() ? '存在' : '不存在'}`);
|
||||||
|
if (!alipan.isLoggedIn()) {
|
||||||
|
return c.json({error: '用户尚未登录成功,请先完成扫码登录'}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
const userInfo = await alipan.getUserInfo();
|
||||||
|
if (!userInfo) {
|
||||||
|
return c.json({error: '获取用户信息失败,可能是token已过期'}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取网盘信息
|
||||||
|
const driveInfo = await alipan.getDriveInfo();
|
||||||
|
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
user_info: userInfo,
|
||||||
|
drive_info: driveInfo,
|
||||||
|
access_token: alipan.getToken(),
|
||||||
|
refresh_token: alipan.getRefreshToken()
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取用户信息失败:', error);
|
||||||
|
return c.json({error: '获取用户信息失败'}, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 退出登录接口
|
||||||
|
export async function logout(c: Context) {
|
||||||
|
try {
|
||||||
|
const sessionId = c.req.query('session_id');
|
||||||
|
if (!sessionId) {
|
||||||
|
return c.json({error: '缺少session_id参数'}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientFingerprint = c.req.header('X-Client-Fingerprint') || c.req.header('User-Agent');
|
||||||
|
|
||||||
|
// 验证会话所有权
|
||||||
|
if (!validateSessionOwnership(sessionId, clientFingerprint)) {
|
||||||
|
return c.json({error: '会话验证失败'}, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除会话
|
||||||
|
const deleted = loginSessions.delete(sessionId);
|
||||||
|
console.log(`会话 ${sessionId} 退出登录: ${deleted ? '成功' : '会话不存在'}`);
|
||||||
|
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
message: '退出登录成功'
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('退出登录失败:', error);
|
||||||
|
return c.json({error: '退出登录失败'}, 500);
|
||||||
|
}
|
||||||
|
}
|
21
src/index.ts
21
src/index.ts
@ -5,6 +5,7 @@ import manifest from '__STATIC_CONTENT_MANIFEST'
|
|||||||
import * as local from "hono/cookie";
|
import * as local from "hono/cookie";
|
||||||
import * as oneui from './oneui';
|
import * as oneui from './oneui';
|
||||||
import * as aliui from './aliui';
|
import * as aliui from './aliui';
|
||||||
|
import * as aliui2 from './aliui2';
|
||||||
import * as ui115 from './115ui';
|
import * as ui115 from './115ui';
|
||||||
import * as ui123 from './123ui';
|
import * as ui123 from './123ui';
|
||||||
import * as baidu from './baidu';
|
import * as baidu from './baidu';
|
||||||
@ -40,6 +41,26 @@ app.get('/alicloud/callback', async (c: Context) => {
|
|||||||
return aliui.alyToken(c);
|
return aliui.alyToken(c);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 阿里云盘扫码2 - 生成二维码 ##############################################################################
|
||||||
|
app.get('/alicloud2/generate_qr', async (c: Context) => {
|
||||||
|
return aliui2.generateQR(c);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 阿里云盘扫码2 - 检查登录状态 ##############################################################################
|
||||||
|
app.get('/alicloud2/check_login', async (c: Context) => {
|
||||||
|
return aliui2.checkLogin(c);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 阿里云盘扫码2 - 获取用户信息 ##############################################################################
|
||||||
|
app.get('/alicloud2/get_user_info', async (c: Context) => {
|
||||||
|
return aliui2.getUserInfo(c);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 阿里云盘扫码2 - 退出登录 ##############################################################################
|
||||||
|
app.get('/alicloud2/logout', async (c: Context) => {
|
||||||
|
return aliui2.logout(c);
|
||||||
|
});
|
||||||
|
|
||||||
// 登录申请 ##############################################################################
|
// 登录申请 ##############################################################################
|
||||||
app.get('/baiduyun/requests', async (c: Context) => {
|
app.get('/baiduyun/requests', async (c: Context) => {
|
||||||
return baidu.oneLogin(c);
|
return baidu.oneLogin(c);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user