# 使用 MetaMask 登入第三方網站 **Published by:** [0x0016區塊猿](https://paragraph.com/@0x0016/) **Published on:** 2022-04-03 **URL:** https://paragraph.com/@0x0016/metamask ## Content 本文亦發佈在作者網站: 最後更新:2021-04-06 https://www.frank.hk/blog/metamask-login/ 當我們逐漸邁向 Web3.0 的今天,原本的網站登入需要透過用戶名密碼的方式,現在又多了一個選擇。只要有一個數字貨幣錢包,便可以使用這個錢包作為身分認證的方式,完全不需要記住複雜的用戶名和密碼。 在開始之前,不妨先看看我製作的一個 MetaMask 登入示範: 按此前往 MetaMask 是一個非常出名的數字貨幣錢包,在 Chrome 上可以安裝它的官方 Extension。另外登入頁面需要使用 web3js , 可以在 github 上下載。也可以直接使用 CDN :<script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script> 整個登入流程,包括幾個基本步驟,但其實並不複雜,可以簡單的用下面的圖示來表示。flow登入流程(瀏覽器)檢測MetaMask使用錢包登入的先決條件是用戶必須安裝 MetaMask Extension。因此第一步必須檢測瀏覽器是否已經安裝 MetaMask: $(function(){ if (window.web3) { // 有安裝 MetaMask } else { alert( '沒有安裝 MetaMask' ); } }); (瀏覽器)連接錢包檢測到已經安裝 MetaMask 之後,便可以嘗試連接錢包和獲得錢包地址。這個步驟需要用戶授權。 window.web3 = new Web3(window.web3.currentProvider); ethereum.request({ method: 'eth_requestAccounts' }).then((result) => { address = result[0]; alert('錢包連接成功'); }).catch((error) => { console.log("error",error); }); 透過調用 ethereum.request({ method: 'eth_requestAccounts' }) , 可以讓 MetaMask 彈出授權連接錢包的畫面,用戶可以在改畫面選擇想綁定的錢包賬戶。screen 20210406104830 2x授權連接 MetaMask 當取得用戶授權後,透過回調的 address = result[0]; 便可以取得錢包地址。(伺服器)取得 nonce取得錢包地址後,便可以繼續開始登入認證。我們要做的是要確定當前的操作者是這個地址的擁有人。 因為只有這個地址的擁有人才擁有錢包對應的私鑰,因此我們可以透過簽名認證的方式,讓用戶對一段信息使用其錢包的私鑰進行簽名,之後在伺服器端,使用錢包的地址(即公鑰)對簽名信息進行驗證。 首先我們需要取得一個 nonce,即「無意義信息」。這個信息必須要由伺服器端產生,並存儲在記憶體中以便下一步驗證時使用。 例子中我們使用 url: "/api/metamask/nonce/"+address 這個 API 來取得 nonce。(瀏覽器)對 nonce 進行簽名在取得了 nonce 之後,便需要用戶使用其私鑰對該 nonce 進行簽名認證。 這一步可以透過調用 window.web3.eth.personal.sign 進行。其中第一個參數是經過 hex 後的待簽名數據,即 nonce, 第二個參數是錢包地址,回調參數則包括簽名結果和簽名後的 signature。 Hexdata 可以透過 window.web3.utils.utf8ToHex( originData ) 來取得。 var hexData = window.web3.utils.utf8ToHex(nonce); window.web3.eth.personal.sign(hexData, address, function(result, signature){ console.log(result); // 結果 console.log(signature); // Signature } (伺服器)驗證簽名取得用戶對 nonce 的簽名後,伺服器就可以透過用戶的公鑰(即錢包地址), 對簽名進行驗證,看看 signature 和剛才上一步產生的 nonce 是否匹配,若匹配,則認為用戶是該地址的擁有者,可以授權用戶登入系統。 伺服器對簽名信息的驗證,不同的程式語言做法會不同。這裡以 Java 為例:public static final String PERSONAL_MESSAGE_PREFIX = "\u0019Ethereum Signed Message:\n"; public static boolean validate(String signature, String message, String address) { String prefix = PERSONAL_MESSAGE_PREFIX + message.length(); byte[] msgHash = Hash.sha3((prefix + message).getBytes()); byte[] signatureBytes = Numeric.hexStringToByteArray(signature); byte v = signatureBytes[64]; if (v < 27) { v += 27; } Sign.SignatureData sd = new Sign.SignatureData( v, Arrays.copyOfRange(signatureBytes, 0, 32), Arrays.copyOfRange(signatureBytes, 32, 64)); String addressRecovered = null; boolean match = false; // Iterate for each possible key to recover for (int i = 0; i < 4; i++) { BigInteger publicKey = Sign.recoverFromSignature( (byte) i, new ECDSASignature(new BigInteger(1, sd.getR()), new BigInteger(1, sd.getS())), msgHash); if (publicKey != null) { addressRecovered = "0x" + Keys.getAddress(publicKey); if (addressRecovered.equals(address)) { match = true; break; } } } return match; } 簽名驗證的方法會比較繁瑣。以上的示例代碼是參考這篇文章的做法,其它的語言可以參考驗證邏輯做相應的處理。要注意 MetaMask 簽名的信息會自動包含一個 Prefix,\u0019Ethereum Signed Message:\n,這點在驗證時要做同樣的處理。 當伺服器成功驗證瀏覽器傳送過來的地址和 signature 後,便可以認為用戶登入成功,可以進行後續的授權操作。問題使用數字貨幣錢包登入,因為需要安裝 extension,目前僅能夠在電腦端進行。暫時無法在手機瀏覽器上運行。要在手機上使用,則必須使用 MetaMask 的手機 App 內建的瀏覽器才可。 技術交流,其他諮詢等,請按此聯絡。 ## Publication Information - [0x0016區塊猿](https://paragraph.com/@0x0016/): Publication homepage - [All Posts](https://paragraph.com/@0x0016/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@0x0016): Subscribe to updates