原文转载自「刘悦的技术博客」https://v3u.cn/a_id_213

上世纪九十年代,海湾战争的时候,一位美军军官担心他们的五角大楼会被敌人的一枚导弹干掉,从而导致在全球的美军基地处于瘫痪状态。这时候,有一位天才的科学家说,最好的中心就是没有中心。是的,这就是最朴素的去中心化思想,于是互联网出现了。一个没有互联网的时代是无法想象的,互联网的核心就是把一个信息分成若干的小件,用不同的途径传播出去,怎么方便怎么走。

三十年后的今天,去中心化身份逐渐被广泛采用。用户的部分在线活动在链上是公开的,可通过加密钱包搜索到,用户在链上创造、贡献、赚取和拥有的东西,都反映了他们的喜好,也逐渐积累成该用户的身份和标识。

当我们的用户厌倦了传统的电子邮件/密码注册流程时,他们会选择Google、GitHub等社交登录方式,这种方式虽然节约了用户的时间,但登录信息也会被第三方平台记录,也就是说我们用平台账号做了什么,平台都会一目了然,甚至还会对我们的行为进行分析、画像。那么有没有一种登录方式,它的所有信息都只保存在客户端和后端,并不牵扯三方平台授权,最大化的保证用户隐私呢?Web3.0给我们提供了一种选择:MetaMask。

MetaMask

MetaMask是用于与以太坊区块链进行交互的软件加密货币钱包。MetaMask允许用户通过浏览器插件或移动应用程序访问其以太坊钱包,然后可以使用这些扩展程序与去中心化应用程序进行交互。当然了,首先需要拥有一个MetaMask钱包,进入https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn

安装metamask浏览器插件:

随后点开插件,创建账号,记录密码、钱包地址、以及助记词等信息。

安装好插件之后,我们就可以利用这个插件和网站应用做交互了。

钱包登录流程

登录逻辑和传统的三方登录还是有差异的,传统三方登录一般是首先跳转三方平台进行授权操作,随后三方平台将code验证码返回给登录平台,登录平台再使用code请求三方平台换取token,再通过token请求用户账号信息,而钱包登录则是先在前端通过Web3.js浏览器插件中保存的私钥对钱包地址进行签名操作,随后将签名和钱包地址发送到后端,后端利用Web3的库用同样的算法进行验签操作,如果验签通过,则将钱包信息存入token,并且返回给前端。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qKbOhOtd-1653997611778)(https://v3u.cn/v3u/Public/js/editor/attached/20220531190527_32835.png)]

前端签名操作

首先需要下载前端的Web3.0操作库,https://docs.ethers.io/v4/,随后集成到登录页面中:

<script src="{{ static_url("js/ethers-v4.min.js") }}"></script>
<script src="{{ static_url("js/axios.js") }}"></script>
<script src="{{ static_url("js/vue.js") }}"></script>

这里我们基于Vue.js配合Axios使用。

接着声明登录激活方法:

sign_w3:function(){  

                    that = this;
ethereum.enable().then(function () { this.provider = new ethers.providers.Web3Provider(web3.currentProvider); this.provider.getNetwork().then(function (result) {
if (result['chainId'] != 1) { console.log("Switch to Mainnet!") } else { // okay, confirmed we're on mainnet this.provider.listAccounts().then(function (result) {
console.log(result);
this.accountAddress = result[0]; // figure out the user's Eth address
this.provider.getBalance(String(result[0])).then(function (balance) {
var myBalance = (balance / ethers.constants.WeiPerEther).toFixed(4);
console.log("Your Balance: " + myBalance);
}); // get a signer object so we can do things that need signing
this.signer = provider.getSigner(); var rightnow = (Date.now()/1000).toFixed(0)
var sortanow = rightnow-(rightnow%600) this.signer.signMessage("Signing in to "+document.domain+" at "+sortanow, accountAddress, "test password!")
.then((signature) => { that.handleAuth(accountAddress,signature);
}); console.log(this.signer);
})
}
})
}) },

通过使用signMessage方法返回签名,这里加签过程中使用基于时间戳的随机数防止未签名,当前端签名生成好之后,立刻异步请求后台接口:

//检查验证
handleAuth:function(accountAddress, signature){ this.myaxios("/checkw3/","post",{"public_address":accountAddress,"signature":signature}).then(data =>{ if(data.errcode==0){
alert("欢迎:"+data.public_address);
localStorage.setItem("token",data.token);
localStorage.setItem("email",data.public_address);
window.location.href = "/";
}else{
alert("验证失败");
}
}); }

这里将当前账户的钱包地址和签名传递给后端,如图所示:

完整页面代码:

<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="utf-8">
<title>Edu</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover">
<link rel="stylesheet" href="{{ static_url("css/min.css") }}" >
<link rel="icon" href="/static/img/favicon.68cbf4197b0c.png">
<script src="{{ static_url("js/ethers-v4.min.js") }}"></script>
<script src="{{ static_url("js/axios.js") }}"></script>
<script src="{{ static_url("js/vue.js") }}"></script>
</head> <body>
<div> {% include "head.html" %} <div id="app" class="container main-content"> <div class="row justify-content-center">
<div class="col-md-10 col-lg-8 article">
<div class="article-body page-body mx-auto" style="max-width: 400px;">
<h1 class="text-center mb-4">Sign-in</h1>
<div class="socialaccount_ballot">
<div class="text-center mb-3">
<ul class="list-unstyled">
<li>
<a @click="sign_w3()" title="GitHub" class="socialaccount_provider github btn btn-secondary btn-lg w-100" href="JavaScript:void(0)">Connect With <strong>Meta Mask</strong></a>
</li>
<li>
<a title="GitHub" class="socialaccount_provider github btn btn-secondary btn-lg w-100" href="https://github.com/login/oauth/authorize?client_id=249b69d8f6e63efb2590&redirect_uri=http://localhost:8000/github_back/">Connect With <strong>GitHub</strong></a>
</li>
</ul>
</div>
<div class="text-center text-muted my-3">— or —</div>
</div> <div class="form-group">
<div id="div_id_login" class="form-group">
<label for="id_login" class=" requiredField">
Email<span class="asteriskField">*</span>
</label>
<div class="">
<input type="email" v-model="email" placeholder="" autocomplete="email" autofocus="autofocus" class="textinput textInput form-control" >
</div>
</div>
</div>
<div class="form-group">
<div id="div_id_password" class="form-group">
<label for="id_password" class=" requiredField">
Password<span class="asteriskField">*</span>
</label>
<div class=""> <input type="password" v-model="password" placeholder="" autocomplete="current-password" minlength="8" maxlength="99" class="textinput textInput form-control" >
</div>
</div>
</div> <div class="text-center">
<button class="btn btn-primary btn-lg text-wrap px-5 mt-2 w-100" name="jsSubmitButton" @click="sign_on">Sign-In</button>
</div> </div>
</div>
</div> </div> {% include "foot.html" %} </div> <script> const App = {
data() {
return {
email:"",
password:"", provider:null,
accountAddress:"",
signer:null
};
},
created: function() { },
methods: {
//metamask登录
sign_w3:function(){ that = this;
ethereum.enable().then(function () { this.provider = new ethers.providers.Web3Provider(web3.currentProvider); this.provider.getNetwork().then(function (result) {
if (result['chainId'] != 1) { console.log("Switch to Mainnet!") } else { // okay, confirmed we're on mainnet this.provider.listAccounts().then(function (result) {
console.log(result);
this.accountAddress = result[0]; // figure out the user's Eth address
this.provider.getBalance(String(result[0])).then(function (balance) {
var myBalance = (balance / ethers.constants.WeiPerEther).toFixed(4);
console.log("Your Balance: " + myBalance);
}); // get a signer object so we can do things that need signing
this.signer = provider.getSigner(); var rightnow = (Date.now()/1000).toFixed(0)
var sortanow = rightnow-(rightnow%600) this.signer.signMessage("Signing in to "+document.domain+" at "+sortanow, accountAddress, "test password!")
.then((signature) => { that.handleAuth(accountAddress,signature);
}); console.log(this.signer);
})
}
})
}) },
//检查验证
handleAuth:function(accountAddress, signature){ this.myaxios("/checkw3/","post",{"public_address":accountAddress,"signature":signature}).then(data =>{ if(data.errcode==0){
alert("欢迎:"+data.public_address);
localStorage.setItem("token",data.token);
localStorage.setItem("email",data.public_address);
window.location.href = "/";
}else{
alert("验证失败");
}
}); },
sign_on:function(){ if(this.email == ""){
alert("邮箱不能为空");
return false;
} if(this.password == ""){
alert("密码不能为空");
return false;
} //登录
this.myaxios("/user_signon/","get",{"email":this.email,"password":this.password}).then(data =>{ if(data.errcode != 0){ alert(data.msg); }else{
alert(data.msg);
localStorage.setItem("token",data.token);
localStorage.setItem("email",data.email);
window.location.href = "/"; //localStorage.removeItem("token")
} }); }, },
};
const app = Vue.createApp(App);
app.config.globalProperties.myaxios = myaxios;
app.config.globalProperties.axios = axios;
app.config.compilerOptions.delimiters = ['${', '}']
app.mount("#app"); </script> </body> </html>

Tornado后端验签:

有人说,既然钱包私钥是存储在浏览器中,也就是保存在客户端,那签名已经通过私钥生成了,为什么还要过一遍后端呢?这不是多此一举吗?事实上,攻击者完全可能获取到前端生成的所有信息,所以签名一定必须得是后端提供,或者至少有一步后端验证,比如著名的微信小程序获取openid问题

后端我们使用异步框架Tornado,配合web3库进行调用,首先安装依赖:

pip3 install tornado==6.1
pip3 install web3==5.29.1

随后创建异步视图方法:



from tornado.web import url
import tornado.web
from tornado import httpclient
from .base import BaseHandler from web3.auto import w3
from eth_account.messages import defunct_hash_message
import time class CheckW3(BaseHandler): async def post(self): public_address = self.get_argument("public_address")
signature = self.get_argument("signature") domain = self.request.host
if ":" in domain:
domain = domain[0:domain.index(":")] now = int(time.time())
sortanow = now-now%600 original_message = 'Signing in to {} at {}'.format(domain,sortanow)
print("[+] checking: "+original_message)
message_hash = defunct_hash_message(text=original_message)
signer = w3.eth.account.recoverHash(message_hash, signature=signature) if signer == public_address:
try:
user = await self.application.objects.get(User,email=public_address)
except Exception as e:
user = await self.application.objects.create(User,email=public_address,password=create_password("third"),role=1) myjwt = MyJwt()
token = myjwt.encode({"id":user.id})
self.finish({"msg":"ok","errcode":0,"public_address":public_address,"token":token})
else:
self.finish({"msg":"could not authenticate signature","errcode":1})

这里通过recoverHash方法对签名进行反编译操作,如果反编译后的钱包地址和前端传过来的钱包地址温和,那么说明当前账户的身份验证通过:

当验签通过之后,利用钱包地址在后台创建账号,随后将钱包地址、token等信息返回给前端,前端将其保存在stroage中即可。

结语

没错,将至已至,未来已来,是时候将Web3.0区块链技术融入产品了,虽然有些固有的思维方式依然在人们的脑海挥之不去,但世界却在时不我待地变化着,正是:青山遮不住,毕竟东流去!项目开源在https://github.com/zcxey2911/Tornado6_Vuejs3_Edu ,与君共觞。

原文转载自「刘悦的技术博客」 https://v3u.cn/a_id_213

青山不遮,毕竟东流,集成Web3.0身份钱包MetaMask以太坊一键登录(Tornado6+Vue.js3)的更多相关文章

  1. 鲜衣怒马散尽千金,Vue3.0+Tornado6前后端分离集成Web3.0之Metamask钱包区块链虚拟货币三方支付功能

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_219 不得不承认,大多数人并不拥有或者曾经拥有加密货币.是的,Web3.0.加密货币.区块链,对于大多数的互联网用户来说,其实是一 ...

  2. 区块相隔虽一线,俱在支付同冶熔,Vue3.0+Tornado6前后端分离集成Web3.0之Metamask区块链虚拟三方支付功能

    最近几年区块链技术的使用外延持续扩展,去中心化的节点认证机制可以大幅度改进传统的支付结算模式的经营效率,降低交易者的成本并提高收益.但不能否认的是,区块链技术也存在着极大的风险,所谓身怀利器,杀心自起 ...

  3. 在PHP应用中简化OAuth2.0身份验证集成:OAuth 2.0 Client

    在PHP应用中简化OAuth2.0身份验证集成:OAuth 2.0 Client   阅读目录 验证代码流程 Refreshing a Token Built-In Providers 这个包能够让你 ...

  4. 以太坊 web3.js 文档翻译及说明

    这些天,为了录制以太坊DAPP开发实战课程,我准备把web3文档全部翻译一下(并做适当的补充),目前web3.js 0.20.x 版本 已经翻译完成,欢迎大家前往查阅. 这里还几个实用DEMO,供大家 ...

  5. 海康威视Web3.0控件个人开发经验及问题总结

    最近在给公司平台写视频监控的页面需求,于是接触到了海康威视的视频控件,网上查阅一番资料后,发现有很多大佬们给出了简易的海康视频控件的上手方法,但是发现仍然有很多地方没有总结到,于是在这里对我个人对海康 ...

  6. Web3.0应用程序架构

    Web 3.0 应用程序(或"DApps")的架构与 Web 2.0 应用程序完全不同. 以博客园为例,这是一个简洁的博客网站,用户可以发布自己的内容并可以评论他人的内容进行互动. ...

  7. [转]Web3.0时代,企业知识管理新趋势

    [转自http://www.amt.com.cn/html/ManageFront/AMTPoint0/2014/0716/1370.html] Web3.0时代,企业知识管理新趋势 2014-07- ...

  8. 以太坊(Ethereum)智能合约NodeJS/Web3 使用

    一.概述 运行环境:Node.js.npm.Truffle.Solidity等 root@keke:~/go-ethereum# node -v v8.9.4 root@keke:~/go-ether ...

  9. Webx.0-Web3.0:Web3.0

    ylbtech-Webx.0-Web3.0:Web3.0 Web3.0只是由业内人员制造出来的概念词语,最常见的解释是,网站内的信息可以直接和其他网站相关信息进行交互,能通过第三方信息平台同时对多家网 ...

随机推荐

  1. 个人作业——体温上报app(二阶段)

    Code.java package com.example.helloworld; import android.graphics.Bitmap; import android.graphics.Ca ...

  2. 使用 Postman 实现 API 自动化测试

    背景介绍 相信大部分开发人员和测试人员对 postman 都十分熟悉,对于开发人员和测试人员而言,使用 postman 来编写和保存测试用例会是一种比较方便和熟悉的方式.但 postman 本身是一个 ...

  3. Docker权限 “Got permission denied while trying to connect to the Docker daemon socket at unix:///var/”

    问题及解决办法 在普通用户下执行docker命令需要用sudo,没加sudo出现了下图所示的提示: 从上图看出,权限不足连接/var/run/docker.sock,我们看下这个文件: 可以看出,这个 ...

  4. C# 类继承中的私有字段都去了哪里?

    最近在看 C++ 类继承中的字段内存布局,我就很好奇 C# 中的继承链那些 private 字段都哪里去了? 在内存中是如何布局的,毕竟在子类中是无法访问的. 一:举例说明 为了方便讲述,先上一个例子 ...

  5. ”只用 1 分钟“ - 超简极速 Apk 签名 & 多渠道打包神器

    众所周知,渠道包作为当下国内 Android 应用市场常见的分发方式,当 APP 和后台交互或进行数据上报时,会带上各自的 channel 渠道信息,以此方便企业 & 开发者统计 APP 在各 ...

  6. VMware Workstation 虚拟机详细安装教程

    一.介绍篇 VMware Workstation 16 Pro是VMware(威睿公司)于2021年最新发布的一代虚拟机软件,软件的中文名是"VMware 工作站 16 专业版". ...

  7. IE让我首次遭受了社会的毒打

    2022年6月15日,微软终止对IE的支持,自此IE走入历史,可以说这是一个时代的终结. 自己在 2011 年刚从业时,IE 在国内的市场占有率可是遥遥领先的,下图来自于 StatCounter 网站 ...

  8. .NET程序配置文件操作(ini,cfg,config)

    在程序开发过程中,我们一般会用到配置文件来设定一些参数.常见的配置文件格式为 ini, xml, config等. INI .ini文件,通常为初始化文件,是用来存储程序配置信息的文本文件. [Log ...

  9. Nacos配置失败(java.lang.IllegalStateException: failed to req API:/nacos/v1/ns/instance after all server)

    解决: nacos服务器过载,可以删掉nacos文件夹下的data文件夹,重新启动.

  10. android stdio开发抖音自动点赞案例

    最近做了一个安卓开发自动刷抖音. 点赞. 评论等等养号行为. 总结一下知识点和遇到的一些问题: 知识点: 1. 使用acessibility mode 对抖音自动化操作. android stdio中 ...