原文转载自「刘悦的技术博客」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. Python数据分析--Numpy常用函数介绍(3)

    摘要:先汇总相关股票价格,然后有选择地对其分类,再计算移动均线.布林线等. 一.汇总数据 汇总整个交易周中从周一到周五的所有数据(包括日期.开盘价.最高价.最低价.收盘价,成交量等),由于我们的数据是 ...

  2. .net6.0 初探

    概述:大概的了解一下 dotnet 6.0 建立 MVC web项目的过程以及程序调用  结合 EF 框架进行简单 的CRUD 1.选择创建  MVC 的Web项目 2.框架类型选择 6.0 3. 6 ...

  3. poj3784(对顶堆)

    题意:多组数据,让你求出1~i(i为奇数&&i<=n)的中位数 思路:首先复杂度必为O(n)或O(nlogn)的(数据范围) 思索,如果题目要求1次中位数,好求!排个序,取a[( ...

  4. 当运行npm install 命令的时候带上ignore-scripts,会发生什么?

    摘要:运行npm install 命令的时候带上ignore-scripts, 可以避免一个恶意包里的病毒. 本文分享自华为云社区<运行npm install 命令的时候带上--ignore-s ...

  5. 1.Shell编程循环语句(if 、while、 until)

    循环语句 for循环语句 读取不同的变量值,用来逐个执行同一组命令 格式: for 变量名 in 取值列表 do 命令序列 done 示例:批量创建用户并设置密码 [root@localhost da ...

  6. Spring Data JPA系列2:SpringBoot集成JPA详细教程,快速在项目中熟练使用JPA

    大家好,又见面了. 这是Spring Data JPA系列的第2篇,在上一篇<Spring Data JPA系列1:JDBC.ORM.JPA.Spring Data JPA,傻傻分不清楚?给你个 ...

  7. 上线项目之局域网上线软件使用-----phpStudy

    上面的图片是phpStudy的软件截图.那么你在哪里会下到呢?链接: https://pan.baidu.com/s/1lvX9jY_K6gGkMOqo76p4nA 提取码: h1it 复制这段内容后 ...

  8. Consider defining a bean of type 'redis.clients.jedis.JedisPool' in your configuration.

    报错信息 原因是没有Jedispool没有注入 import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml ...

  9. jquery通过id和class取值

    一.Jquery通过id获取Input文本框value值 二.Jquery通过id获取文本内容(1) 三.Jquery通过id获取文本内容(2) 四.Jquey通过class获取文本内容 (注:Jqu ...

  10. [BJDCTF2020]The mystery of ip|[CISCN2019 华东南赛区]Web11|SSTI注入

    记录一下BUUCTF中两个类似的SSTI注入关卡 [BJDCTF2020]The mystery of ip-1: 1.打开之后显示如下: 2.在hint.php中进行了相关提示,如下: 3.既然获取 ...