NetEaseCloudMusicApi
項目源地址:
https://github.com/JabinGP/NetEaseCloudMusicApi
小程序網易雲音樂api模塊
之前我們已經開發過一款小程序適用的qq音樂api庫 https://github.com/FisherWY/QQMusicPlugin
這次開發網易雲音樂api庫的原因是qq音樂api庫在小程序中iOS環境下無法使用小程序提供的背景音頻播放器播放的問題
網易雲的加密算法真的比其他幾家api復雜太多了!!!!完爆QQ和酷狗
依賴
本api庫參考了Github上面開源的node庫,因為我們只想要查找音樂和播放音樂這兩個功能
雖然Github那個庫很方便,但是我們不想為了兩個接口特意去跑一個node.js服務。Github上的庫
- big-integer.js
這里注意,不要使用最新版的,最新版的庫再模擬器上運行沒有問題,但是在真機調試的上傳包階段會報錯說無法識別big-integer.js
最后在我的嘗試下,選用了一個老版本的庫解決了這個問題。 - crypto.js
這個庫是用來aes加密的,在node上面有一個原生的crypto,但是在小程序里我們沒有,所以我照着Github上的源碼一點一點用這個庫翻譯過來的
還有Buffer在小程序里也沒有,我使用這個庫的方法代替了。
獲取api的原理
網上很多帖子講的很清楚了,這里推薦幾篇文章,我只做一個簡單的總結,方便大家理解這個庫。
網易雲的加密算法大概使用了兩個:
- AES加密+BASE64編碼
- RSA加密
加密大致流程:
- api請求信息先被轉成json字符串格式,然后再使用一個固定的密鑰aes+base64編碼加密,得到了第一個
加密結果a
。 - 客戶端從
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/
隨機生成一個新的16位密鑰,然后用這個密鑰去加密加密結果a
,得到加密結果b
。
3.這樣我們的數據就被雙重加密了,但是我們要發給服務器去查詢對應的數據,服務器知道第一個固定的密鑰是多少,可以解開第一個加密結果,但是服務器可不知道我們第二次加密用的是什么,所以服務器還需要得到我們的第二個生成的隨機加密密鑰。 - 第二個隨機加密密鑰要是直接發給服務器好像就不太安全了,所以客戶端對第二個隨機加密密鑰也進行了加密,使用的就是RSA加密,加密后得到的數據我們稱為
c
- 將
b
和c
發送給服務器,服務器就會返回給我們對應的結果了。
加密核心代碼
這段代碼傳入對象后可以直接加密成符合網易雲api加密的結果。
// 生成隨機數,size默認16 function createSecretKey(size) { const keys = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" let key = "" for (let i = 0; i < size; i++) { let pos = Math.random() * keys.length pos = Math.floor(pos) key = key + keys.charAt(pos) } return key } // aes加密方法 function aesEncrypt(word, secKey) { let key = CryptoJS.enc.Utf8.parse(secKey); //十六位十六進制數作為密鑰 let iv = CryptoJS.enc.Utf8.parse(aes_mv); //十六位十六進制數作為密鑰偏移量 let srcs = CryptoJS.enc.Utf8.parse(word); let encrypted = CryptoJS.AES.encrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); let res = encrypted.toString(); console.log(res); return res; } // 填充方法 function zfill(str, size) { while (str.length < size) str = "0" + str return str } // rsa加密方法 function rsaEncrypt(text, pubKey, modulus) { const _text = text.split('').reverse().join('') const biText = bigInt(CryptoJS.enc.Utf8.parse(_text).toString(), 16), biEx = bigInt(pubKey, 16), biMod = bigInt(modulus, 16), biRet = biText.modPow(biEx, biMod) return zfill(biRet.toString(16), 256) } // 加密總入口 function Encrypt(obj) { const text = JSON.stringify(obj) const secKey = createSecretKey(16) const encText = aesEncrypt(aesEncrypt(text, nonce), secKey) const encSecKey = rsaEncrypt(secKey, pubKey, modulus) return