使用以太坊地址登录网页
前端使用 web3.js
获取用户地址并进行签名。
后端使用 go-ethereum
库校验签名是否来自指定地址,大致校验流程:
- 通过明文的HASH和签名数据算出公钥
- 通过公钥还原出地址
- 校验还原出来的地方是否与用户指定地址相同
前端:
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
let address = '';
$(function () {
$('body').on('click', '#verify', function () { // 点击 验证 按钮后的事件
let nonce = "Hello,World!"; // 假如这里是从服务端获取到的随机字符串
// let hexNonce = window.web3.utils.utf8ToHex(nonce);
let nonceHash = web3.utils.sha3(nonce);
console.log('对随机数据签名:', nonceHash, ",addr:", address);
window.web3.eth.personal.sign(nonceHash, address, function (result, signature) {
console.log("result:", result, ",signature:", signature);
$('#verify_result').append('<p>验证签名:' + signature + '</p>');
//TODO: 将签名发送到服务端,服务端通过公钥验证签名是否正确
});
});
if (!window.web3) {
alert('沒有找到 web3 库,请安装钱包插件');
return;
}
window.web3 = new Web3(window.web3.currentProvider);
ethereum.request({ method: 'eth_requestAccounts' }).then((result) => {
console.log('连接钱包得到的 result:', result);
address = result[0];
$('#address').html(address + ' <span id="verify">点击验证</span>');
}).catch((error) => {
console.log("error", error);
});
});
</script>
</head>
<body>
<div id="address"></div>
<div id="verify_result"></div>
</body>
</html>
后端:
package main
import (
"fmt"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)
func verifySig(from, sigHex, msg string) bool {
msgHash := crypto.Keccak256Hash([]byte(msg)).Bytes()
fromAddr := common.HexToAddress(from)
sig := hexutil.MustDecode(sigHex)
if sig[crypto.RecoveryIDOffset] != 27 && sig[crypto.RecoveryIDOffset] != 28 {
return false
}
sig[crypto.RecoveryIDOffset] -= 27
pubKey, err := crypto.SigToPub(accounts.TextHash(msgHash), sig)
if err != nil {
fmt.Println("crypto.SigToPub() fail:", err)
return false
}
recoveredAddr := crypto.PubkeyToAddress(*pubKey)
fmt.Println("fromAddr:", fromAddr)
fmt.Println("recoveredAddr:", recoveredAddr)
return fromAddr == recoveredAddr
}