mirror of
https://gitlab.com/Binaryify/neteasecloudmusicapi.git
synced 2025-05-23 22:37:41 +08:00
增加二维码登录
This commit is contained in:
parent
bf50fa65eb
commit
77775b25f5
@ -18,6 +18,8 @@
|
||||
|
||||
[sqaiyan/netmusic-node](https://github.com/sqaiyan/netmusic-node)
|
||||
|
||||
[greats3an/pyncm](https://github.com/greats3an/pyncm)
|
||||
|
||||
|
||||
## 环境要求
|
||||
|
||||
|
@ -373,7 +373,7 @@ $ sudo docker run -d -p 3000:3000 netease-music-api
|
||||
|
||||
### 登录
|
||||
|
||||
说明 : 登录有两个接口,建议使用`encodeURIComponent`对密码编码或者使用`POST`请求,避免某些特殊字符无法解析,如`#`(`#`在url中会被识别为hash,而不是query)
|
||||
说明 : 登录有三个接口,建议使用`encodeURIComponent`对密码编码或者使用`POST`请求,避免某些特殊字符无法解析,如`#`(`#`在url中会被识别为hash,而不是query)
|
||||
|
||||
#### 1. 手机登录
|
||||
|
||||
@ -419,6 +419,24 @@ v3.30.0后支持手动传入cookie,登录接口返回内容新增 `cookie` 字
|
||||
cookie:"xxx"
|
||||
}
|
||||
```
|
||||
#### 3. 二维码登录
|
||||
说明: 二维码登录涉及到3个接口
|
||||
1. 二维码key生成接口
|
||||
说明: 调用此接口可生成一个key
|
||||
**接口地址 :** `/login/qr/key`
|
||||
|
||||
2. 二维码生成接口
|
||||
说明: 调用此接口传入上一个接口生成的key可生成二维码图片的base64和二维码信息,可使用base64展示图片,或者使用二维码信息内容自行使用第三方二维码生产库渲染二维码
|
||||
可选参数: `qrimg` 传入后
|
||||
**接口地址 :** `/login/qr/create`
|
||||
|
||||
|
||||
3. 二维码检测扫码状态接口
|
||||
说明: 轮询此接口可获取二维码扫码状态,801为等待扫码,802为待确认,803为授权登陆成功
|
||||
|
||||
**接口地址 :** `/login/qr/check`
|
||||
|
||||
|
||||
|
||||
#### 注意
|
||||
|
||||
|
34
module/login_qr_check.js
Normal file
34
module/login_qr_check.js
Normal file
@ -0,0 +1,34 @@
|
||||
module.exports = async (query, request) => {
|
||||
const data = {
|
||||
key: query.key,
|
||||
type: 1,
|
||||
}
|
||||
try {
|
||||
let result = await request(
|
||||
'POST',
|
||||
`https://music.163.com/weapi/login/qrcode/client/login`,
|
||||
data,
|
||||
{
|
||||
crypto: 'weapi',
|
||||
cookie: query.cookie,
|
||||
proxy: query.proxy,
|
||||
realIP: query.realIP,
|
||||
},
|
||||
)
|
||||
result = {
|
||||
status: 200,
|
||||
body: {
|
||||
...result.body,
|
||||
cookie: result.cookie.join(';'),
|
||||
},
|
||||
cookie: result.cookie,
|
||||
}
|
||||
return result
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 200,
|
||||
body: {},
|
||||
cookie: result.cookie,
|
||||
}
|
||||
}
|
||||
}
|
18
module/login_qr_create.js
Normal file
18
module/login_qr_create.js
Normal file
@ -0,0 +1,18 @@
|
||||
const QRCode = require('qrcode')
|
||||
|
||||
module.exports = (query, request) => {
|
||||
return new Promise(async (resolve) => {
|
||||
const url = `https://music.163.com/login?codekey=${query.key}`
|
||||
return resolve({
|
||||
code: 200,
|
||||
status: 200,
|
||||
body: {
|
||||
code: 200,
|
||||
data: {
|
||||
qrurl: url,
|
||||
qrimg: query.qrimg ? await QRCode.toDataURL(url) : '',
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
26
module/login_qr_key.js
Normal file
26
module/login_qr_key.js
Normal file
@ -0,0 +1,26 @@
|
||||
module.exports = async (query, request) => {
|
||||
const data = {
|
||||
type: 1,
|
||||
}
|
||||
const result = await request(
|
||||
'POST',
|
||||
`https://music.163.com/weapi/login/qrcode/unikey`,
|
||||
data,
|
||||
{
|
||||
crypto: 'weapi',
|
||||
cookie: query.cookie,
|
||||
proxy: query.proxy,
|
||||
realIP: query.realIP,
|
||||
},
|
||||
)
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
data: {
|
||||
...result.body,
|
||||
code: 200,
|
||||
},
|
||||
},
|
||||
cookie: result.cookie,
|
||||
}
|
||||
}
|
@ -1,21 +1,26 @@
|
||||
// 登录状态
|
||||
|
||||
module.exports = (query, request) => {
|
||||
return request(
|
||||
'GET',
|
||||
`https://music.163.com`,
|
||||
{},
|
||||
{ cookie: query.cookie, proxy: query.proxy, realIP: query.realIP },
|
||||
).then((response) => {
|
||||
try {
|
||||
let profile = eval(`(${/GUser\s*=\s*([^;]+);/.exec(response.body)[1]})`)
|
||||
let bindings = eval(`(${/GBinds\s*=\s*([^;]+);/.exec(response.body)[1]})`)
|
||||
response.body = { code: 200, profile: profile, bindings: bindings }
|
||||
return response
|
||||
} catch (err) {
|
||||
response.status = 301
|
||||
response.body = { code: 301 }
|
||||
return Promise.reject(response)
|
||||
module.exports = async (query, request) => {
|
||||
const data = {}
|
||||
let result = await request(
|
||||
'POST',
|
||||
`https://music.163.com/weapi/w/nuser/account/get`,
|
||||
data,
|
||||
{
|
||||
crypto: 'weapi',
|
||||
cookie: query.cookie,
|
||||
proxy: query.proxy,
|
||||
realIP: query.realIP,
|
||||
},
|
||||
)
|
||||
if (result.body.code === 200) {
|
||||
result = {
|
||||
status: 200,
|
||||
body: {
|
||||
data: {
|
||||
...result.body,
|
||||
},
|
||||
},
|
||||
cookie: result.cookie,
|
||||
}
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -40,6 +40,7 @@
|
||||
"express": "^4.17.1",
|
||||
"express-fileupload": "^1.1.9",
|
||||
"pac-proxy-agent": "^4.0.0",
|
||||
"qrcode": "^1.4.4",
|
||||
"tunnel": "^0.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
66
public/qrlogin.html
Normal file
66
public/qrlogin.html
Normal file
@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>二维码登录</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<img id="qrImg" />
|
||||
<div id="info" class="info"></div>
|
||||
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js
|
||||
"></script>
|
||||
<script>
|
||||
async function checkStatus(key) {
|
||||
const res = await axios({
|
||||
url: `/login/qr/check?key=${key}&timerstamp=${Date.now()}`,
|
||||
withCredentials: true, //关键
|
||||
})
|
||||
return res.data
|
||||
}
|
||||
async function getLoginStatus() {
|
||||
const res = await axios({
|
||||
url: `/login/status?timerstamp=${Date.now()}`,
|
||||
withCredentials: true, //关键
|
||||
})
|
||||
document.querySelector('#info').innerText = JSON.stringify(res.data, null, 2)
|
||||
}
|
||||
async function login() {
|
||||
let timer
|
||||
let timestamp = Date.now()
|
||||
this.getLoginStatus()
|
||||
const res = await axios({
|
||||
url: `/login/qr/key?timerstamp=${Date.now()}`,
|
||||
withCredentials: true, //关键
|
||||
})
|
||||
const key = res.data.data.unikey
|
||||
const res2 = await axios({
|
||||
url: `/login/qr/create?key=${key}&qrimg=true&timerstamp=${Date.now()}`,
|
||||
withCredentials: true, //关键
|
||||
})
|
||||
document.querySelector('#qrImg').src = res2.data.data.qrimg
|
||||
|
||||
timer = setInterval(async () => {
|
||||
const statusRes = await this.checkStatus(key)
|
||||
if (statusRes.code === 800) {
|
||||
alert('二维码已过期,请重新获取')
|
||||
clearInterval(timer)
|
||||
}
|
||||
if (statusRes.code === 803) {
|
||||
// 这一步会返回cookie
|
||||
clearInterval(timer)
|
||||
alert('授权登录成功')
|
||||
await this.getLoginStatus()
|
||||
}
|
||||
}, 3000)
|
||||
}
|
||||
login()
|
||||
</script>
|
||||
<style>
|
||||
.info{
|
||||
white-space: pre;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
@ -153,7 +153,11 @@ const createRequest = (method, url, data, options) => {
|
||||
try {
|
||||
answer.body = body
|
||||
answer.status = answer.body.code || res.status
|
||||
if (answer.body.code === 502) {
|
||||
if (
|
||||
[201, 302, 400, 502, 800, 801, 802, 803].indexOf(answer.body.code) >
|
||||
-1
|
||||
) {
|
||||
// 特殊状态码
|
||||
answer.status = 200
|
||||
}
|
||||
} catch (e) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user