前端和Java验签以太坊钱包签名实现中心化登录

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 前端和Java验签以太坊钱包签名实现中心化登录

引言

相信做过一些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

  <!--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同学测试,我这里附上 验签代码加密代码,线上只需要验签代码,因为加密代码由前端同学完成了。


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);
    }

}
相关文章
|
2天前
|
机器学习/深度学习 数据采集 JavaScript
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
ADR药品不良反应监测系统是一款智能化工具,用于监测和分析药品不良反应。该系统通过收集和分析病历、处方及实验室数据,快速识别潜在不良反应,提升用药安全性。系统采用Java开发,基于SpringBoot框架,前端使用Vue,具备数据采集、清洗、分析等功能模块,并能生成监测报告辅助医务人员决策。通过集成多种数据源并运用机器学习算法,系统可自动预警药品不良反应,有效减少药害事故,保障公众健康。
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
|
24天前
|
前端开发 JavaScript 应用服务中间件
【uniapp】谷歌授权登录,前端uniapp直调(含源码)
本文介绍如何在uniapp项目中实现谷歌授权登录,无需后端参与。文章分为三部分:1)谷歌授权登录流程,详细说明从用户点击登录到获取用户信息的整个过程;2)谷歌开发者控制台配置,包括创建项目、配置同意屏幕及OAuth客户端ID等步骤;3)uniapp前端实操,提供具体代码示例,展示如何获取授权码并用其交换访问令牌,最终获取用户信息
28 1
【uniapp】谷歌授权登录,前端uniapp直调(含源码)
|
1月前
|
设计模式 前端开发 Java
【前端学java】SpringBootWeb极速入门-分层解耦(03)
【8月更文挑战第13天】SpringBootWeb极速入门-分层解耦(03)
15 2
【前端学java】SpringBootWeb极速入门-分层解耦(03)
|
1月前
|
开发框架 前端开发 Java
【前端学java】SpringBootWeb极速入门-实现一个简单的web页面01
【8月更文挑战第12天】SpringBootWeb极速入门-实现一个简单的web页面01
50 3
【前端学java】SpringBootWeb极速入门-实现一个简单的web页面01
|
1月前
|
前端开发 Java 编译器
【前端学java】如何从前端视角快速学习Maven
【8月更文挑战第12天】如何从前端视角快速学习Maven
40 2
【前端学java】如何从前端视角快速学习Maven
|
1月前
|
前端开发 Java Maven
【前端学java】全网最详细的maven安装与IDEA集成教程!
【8月更文挑战第12天】全网最详细的maven安装与IDEA集成教程!
66 2
【前端学java】全网最详细的maven安装与IDEA集成教程!
|
1月前
|
JSON 前端开发 Java
【前端学java】SpringBootWeb极速入门-请求参数解析(02)
【8月更文挑战第12天】SpringBootWeb极速入门-请求参数解析(02)
15 1
【前端学java】SpringBootWeb极速入门-请求参数解析(02)
|
24天前
|
安全 Java 开发者
【技术咖必看】Java异常处理新境界:throws关键字,打造万无一失的方法签名!
【技术咖必看】Java异常处理新境界:throws关键字,打造万无一失的方法签名!
32 3
|
24天前
|
安全 Java 程序员
【程序猿逆袭指南】Java高手的秘密武器:throws关键字,让你的方法签名霸气侧漏!
【程序猿逆袭指南】Java高手的秘密武器:throws关键字,让你的方法签名霸气侧漏!
15 3
|
1月前
|
前端开发 Java 数据库
Java系列之 Long类型返回前端精度丢失
这篇文章讨论了Java后端实体类中Long类型数据在传递给前端时出现的精度丢失问题,并提供了通过在实体类字段上添加`@JsonSerialize(using = ToStringSerializer.class)`注解来确保精度的解决方法。