引言
相信做过一些dapp项目的小伙伴都知道,当dapp需要和中心化的业务系统交互时,怎么验证登录成了一个问题。要dapp输入登录账户密码就很奇怪,违背了设计初衷,不登录吧,中心化系统又没有安全可言。
故此需要一个特定的动作。只有当钱包持有人授权登录(连接钱包),前端js通过调用特定api加密得到算法,从而传到后台验证签名,从而达到登录效果。
前端js 加密代码(web.js)
// 这里一下没找到前端代码,从别人哪里拿过的-见谅
import {ethers,providers} from 'ethers';
class WalletHolder{
provider:providers.Provider;
signer:providers.JsonRpcSigner;
accounts:Array<string>;
constructor(_provider:providers.Provider,_signer:providers.JsonRpcSigner,_accounts:Array<string>) {
this.provider = _provider;
this.signer = _signer;
this.accounts = _accounts;
}
}
export default class WalletUtils{
public static async metamask() : Promise<Array<any>> {
try {
var provider = new ethers.providers.Web3Provider(window['ethereum']);
var accounts = await provider.send("eth_requestAccounts", []);
var signer = await provider.getSigner();
console.log("Account:", await signer.getAddress());
} catch (error) {
return [null,error]
}
return [new WalletHolder(provider,signer,accounts),null];
}
static holder:WalletHolder;
public static get signer() : providers.JsonRpcSigner {
return WalletUtils.holder.signer;
}
public static get provider() : providers.Provider {
return WalletUtils.holder.provider;
}
public static get accounts() : Array<string> {
return WalletUtils.holder.accounts;
}
public static async init(){
const[holder,error] = await WalletUtils.metamask();
if(error){
console.error('init metamask error',error)
return;
}
if(holder instanceof WalletHolder){
WalletUtils.holder = holder;
console.log('init success')
}
}
}
if(!WalletUtils.holder){
await WalletUtils.init();
}
const defaultSinger = WalletUtils.signer;
//使用签名及逆行
const message = await defaultSinger.signMessage("areyouok!")
//content = "hgqZgU7IuZM8y1rWUQOaMCbbb2QS39EtNWvLGu9FyDvTTQD/cOiLkgYNy+xGl/oEh/idTY2xNh9Kdpmg+ljgfFnd8R8bOEZ2JH38c4Jlhhm6ypntKdVKgrm9dquk8En4sZw1R/mEh8O7sSQ7kLOBv6Epzme0ZGyXJVWA4accjBo=";
// sing= "0xcb0a1688018b8cd8f0f5dd66647de8bb251772bc4aa64c9ac8ffc37af29299462f0ee7715646b8172e381bdf44ac5970180bbc10d31de1b6ec59a09d620fa8e21b";
//walletAddress = "0x37949e80Aedc7d72CFB3667d092161EA8729Ba49";
java验签
引入pom文章来源:https://www.toymoban.com/news/detail-855810.html
<!--web3j-->
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>org.web3j</groupId>
<artifactId>geth</artifactId>
<version>5.0.0</version>
<exclusions>
<exclusion>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.web3j</groupId>
<artifactId>abi</artifactId>
<version>5.0.0</version>
</dependency>
为了方便java同学测试,我这里附上 验签代码和 加密代码,线上只需要验签代码,因为加密代码由前端同学完成了。文章来源地址https://www.toymoban.com/news/detail-855810.html
import org.web3j.crypto.Credentials;
import org.web3j.crypto.Keys;
import org.web3j.crypto.Sign;
import org.web3j.utils.Numeric;
import java.math.BigInteger;
import java.security.SignatureException;
import java.util.Arrays;
/**
* @Description: Web3j签名验签
*/
public class ECRecoverUtil {
public static void main(String[] args) throws SignatureException {
String content = "hgqZgU7IuZM8y1rWUQOaMCbbb2QS39EtNWvLGu9FyDvTTQD/cOiLkgYNy+xGl/oEh/idTY2xNh9Kdpmg+ljgfFnd8R8bOEZ2JH38c4Jlhhm6ypntKdVKgrm9dquk8En4sZw1R/mEh8O7sSQ7kLOBv6Epzme0ZGyXJVWA4accjBo=";
String sing= "0xcb0a1688018b8cd8f0f5dd66647de8bb251772bc4aa64c9ac8ffc37af29299462f0ee7715646b8172e381bdf44ac5970180bbc10d31de1b6ec59a09d620fa8e21b";
String walletAddress = "0x37949e80Aedc7d72CFB3667d092161EA8729Ba49";
boolean mark = validate(sing,content,walletAddress);
System.out.println(sing);
System.out.println(mark);
}
/**
* 签名
*
* @param content 原文信息
* @param privateKey 私钥
*/
public static String signPrefixedMessage(String content, String privateKey) {
// todo 如果验签不成功,就不需要Hash.sha3 直接content.getBytes()就可以了
// 原文信息字节数组
// byte[] contentHashBytes = Hash.sha3(content.getBytes());
byte[] contentHashBytes = content.getBytes();
// 根据私钥获取凭证对象
Credentials credentials = Credentials.create(privateKey);
//
Sign.SignatureData signMessage = Sign.signPrefixedMessage(contentHashBytes, credentials.getEcKeyPair());
byte[] r = signMessage.getR();
byte[] s = signMessage.getS();
byte[] v = signMessage.getV();
byte[] signByte = Arrays.copyOf(r, v.length + r.length + s.length);
System.arraycopy(s, 0, signByte, r.length, s.length);
System.arraycopy(v, 0, signByte, r.length + s.length, v.length);
return Numeric.toHexString(signByte);
}
/**
* 验证签名
*
* @param signature 验签数据
* @param content 原文数据
* @param walletAddress 钱包地址
* @return 结果
*/
public static Boolean validate(String signature, String content, String walletAddress) throws SignatureException {
if (content == null) {
return false;
}
// todo 如果验签不成功,就不需要Hash.sha3 直接content.getBytes()就可以了
// 原文字节数组
// byte[] msgHash = Hash.sha3(content.getBytes());
byte[] msgHash = content.getBytes();
// 签名数据
byte[] signatureBytes = Numeric.hexStringToByteArray(signature);
byte v = signatureBytes[64];
if (v < 27) {
v += 27;
}
//通过摘要和签名后的数据,还原公钥
Sign.SignatureData signatureData = new Sign.SignatureData(
v,
Arrays.copyOfRange(signatureBytes, 0, 32),
Arrays.copyOfRange(signatureBytes, 32, 64));
// 签名的前缀消息到密钥
BigInteger publicKey = Sign.signedPrefixedMessageToKey(msgHash, signatureData);
// 得到公钥(私钥对应的钱包地址)
String parseAddress = "0x" + Keys.getAddress(publicKey);
// 将钱包地址进行比对
return parseAddress.equalsIgnoreCase(walletAddress);
}
}
到了这里,关于前端和Java验签以太坊钱包签名实现中心化登录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!