当我们的用户厌倦了传统的电子邮件/密码注册流程时,他们会选择Google、GitHub等社交登录方式,这种方式虽然节约了用户的时间,但登录信息也会被第三方平台记录,也就是说我们用平台账号做了什么,平台都会一目了然,甚至还会对我们的行为进行分析、画像。那么有没有一种登录方式,它的所有信息都只保存在客户端和后端,并不牵扯三方平台授权,最大化的保证用户隐私呢?Web3.0给我们提供了一种选择:MetaMask。
+ ]+ O7 u! O0 N% A7 R/ f
MetaMask# Y& Z0 h! C# n5 u3 e
MetaMask是用于与以太坊区块链进行交互的软件加密货币钱包。MetaMask允许用户通过浏览器插件或移动应用程序访问其以太坊钱包,然后可以使用这些扩展程序与去中心化应用程序进行交互。当然了,首先需要拥有一个MetaMask钱包,进入https://chrome.google.com/websto ... oehlefnkodbefgpgknn
安装metamask浏览器插件:
9 |5 X- ? O6 r
随后点开插件,创建账号,记录密码、钱包地址、以及助记词等信息。* r; u' o7 ]3 `9 H: _) A
安装好插件之后,我们就可以利用这个插件和网站应用做交互了。; Z1 x0 g; ~3 O- g" o5 P+ X
钱包登录流程 y/ w8 h& D: r5 w) U
登录逻辑和传统的三方登录还是有差异的,传统三方登录一般是首先跳转三方平台进行授权操作,随后三方平台将code验证码返回给登录平台,登录平台再使用code请求三方平台换取token,再通过token请求用户账号信息,而钱包登录则是先在前端通过Web3.js浏览器插件中保存的私钥对钱包地址进行签名操作,随后将签名和钱包地址发送到后端,后端利用Web3的库用同样的算法进行验签操作,如果验签通过,则将钱包信息存入token,并且返回给前端。
4 o% }: z$ t$ C g& k9 u
3 b- A$ Y% B$ A
前端签名操作2 _8 U2 u* ~4 j5 [) s
首先需要下载前端的Web3.0操作库,https://docs.ethers.io/v4/,随后集成到登录页面中:* X2 D) I$ y' A2 H
* ~2 |5 I( S2 b4 n+ ]4 r: t
. T1 f# a: @/ b8 W- ?* t& _
- <script src="{{ static_url("js/ethers-v4.min.js") }}"></script>- y6 C& _. g# R. A& D" t+ ?+ K
- <script src="{{ static_url("js/axios.js") }}"></script>& z3 F7 l" Z# q( }+ ?8 D* D
- <script src="{{ static_url("js/vue.js") }}"></script>
* Q6 C7 v) I" |% f! ^2 s4 A
这里我们基于Vue.js配合Axios使用。0 j; p: I/ _$ q, J
接着声明登录激活方法:
- sign_w3:function(){
- + Q8 L" `! L5 S+ _' V3 d( l
- that = this;
- ethereum.enable().then(function () {
- this.provider = new ethers.providers.Web3Provider(web3.currentProvider);6 Z2 E9 T3 I6 c8 s7 {" D! C& f
- this.provider.getNetwork().then(function (result) {* X1 P! U$ m: r, }4 A
- if (result['chainId'] != 1) {
- console.log("Switch to Mainnet!")+ C& r3 d" w' v4 H8 p
- } else { // okay, confirmed we're on mainnet
- % Y$ w6 b' Z& ~3 B) M1 g: f+ i. }2 |
- this.provider.listAccounts().then(function (result) {
- console.log(result);: i; d/ `* I: B+ P
- this.accountAddress = result[0]; // figure out the user's Eth address& t$ u( _( W- R" g, |" H
- this.provider.getBalance(String(result[0])).then(function (balance) {
- var myBalance = (balance / ethers.constants.WeiPerEther).toFixed(4);
- console.log("Your Balance: " + myBalance);
- });
- % m3 f7 [2 S' X! T7 K0 r. e
- // get a signer object so we can do things that need signing
- this.signer = provider.getSigner();
- var rightnow = (Date.now()/1000).toFixed(0)' F, b" k$ e. E
- var sortanow = rightnow-(rightnow%600)5 D. ], M9 i/ _0 K2 ?7 i( x
- this.signer.signMessage("Signing in to "+document.domain+" at "+sortanow, accountAddress, "test password!")
- .then((signature) => { that.handleAuth(accountAddress,signature);
- });; R( Z3 t" k+ P @& Q. x
- & A% E/ \/ w" s" M: H( G @7 g$ O
- console.log(this.signer);! Z5 X7 m5 Y" Z9 B
- })5 q! R3 n; \5 F$ h' q- A
- }1 P5 G4 C6 o: @
- })- M: g! L2 r. o# J- n
- })2 Q* W8 R* G4 a- k9 d$ T: v2 {: C
- },
4 \6 a& h5 B% s. q6 z1 v1 B- Y
- //检查验证7 g4 q1 t- T8 a* p$ e
- handleAuth:function(accountAddress, signature){
- 5 G* {8 S! I1 y% d+ `
- this.myaxios("/checkw3/","post",{"public_address":accountAddress,"signature":signature}).then(data =>{
- if(data.errcode==0){# E/ d$ Y4 P( u! j. w
- alert("欢迎:"+data.public_address);! {. ?9 ~3 L1 t$ i8 w" P
- localStorage.setItem("token",data.token);
- localStorage.setItem("email",data.public_address);4 x, |, d! b, Z% k! b7 z" x# E
- window.location.href = "/";
- }else{+ l; Z- |* |' k, O
- alert("验证失败");' l( s3 N4 D6 |* B6 J
- }
- });$ {- x/ k$ s1 H3 b1 Y
- i) ]8 s3 J1 s, z" G
- }
- K* Y3 @9 D9 `3 b: N! D
# ?/ z/ Z# d" P4 e
随后创建异步视图方法:! n$ `6 u4 Z& J; {* ?. ^% W2 _7 M
- from tornado.web import url
- import tornado.web" Z" r! J# c2 [& l& h/ v
- from tornado import httpclient% `4 ? F: M4 N* c& s& C# s
- from .base import BaseHandler8 f2 H8 Q, D+ I+ X5 ~; i8 O4 N
- from web3.auto import w3
- from eth_account.messages import defunct_hash_message8 }1 h6 E; g. t% ^$ n
- import time& W+ ?9 d. n# [0 l6 t7 @# d# Z! Y
- 6 P' m/ N( x% P( c+ I3 ~; y
- class CheckW3(BaseHandler):# \' ~. h9 S" W' {' Z7 u
- async def post(self):
- public_address = self.get_argument("public_address")3 S, \, H* O4 f
- signature = self.get_argument("signature")
- . ]) \( O. k( _5 B f! d( o
- domain = self.request.host* {' i! ?7 I, E
- if ":" in domain:
- domain = domain[0:domain.index(":")]
- ( f3 V0 t# E( l; s# z2 u! ^ U
- now = int(time.time())/ ~8 q$ r! Q: ~1 s: D1 H
- sortanow = now-now%600
- 8 m) y( x, _. U
- original_message = 'Signing in to {} at {}'.format(domain,sortanow)9 z" j# V8 \% P+ }$ ^ k
- print("[+] checking: "+original_message)! t6 o/ F1 I* H ~) h) C# C0 c
- message_hash = defunct_hash_message(text=original_message)
- signer = w3.eth.account.recoverHash(message_hash, signature=signature)
- if signer == public_address:- c/ N, h0 C8 F$ A# V
- 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)6 S8 j+ x8 t1 l4 G" M, z7 e% Y0 w
- myjwt = MyJwt()% W3 f7 o+ K0 j
- token = myjwt.encode({"id":user.id})
- self.finish({"msg":"ok","errcode":0,"public_address":public_address,"token":token})
- else:: k! u$ j$ s1 |7 M& S; Q$ W t
- self.finish({"msg":"could not authenticate signature","errcode":1})
这里通过recoverHash方法对签名进行反编译操作,如果反编译后的钱包地址和前端传过来的钱包地址吻合,那么说明当前账户的身份验证通过:. i3 P6 N+ d0 j5 P9 X
, D0 I0 Z/ L! P& W M! u1 J# q7 S5 |
2 X& w- j7 f' V0 r8 m3 D( O3 r4 P
当验签通过之后,利用钱包地址在后台创建账号,随后将钱包地址、token等信息返回给前端,前端将其保存在stroage中即可。
3 O; ] ^: _ @
结语
没错,将至已至,未来已来,是时候将Web3.0区块链技术融入产品了,虽然有些固有的思维方式依然在人们的脑海挥之不去,但世界却在时不我待地变化着,正是:青山遮不住,毕竟东流去!项目开源在https://github.com/zcxey2911/Tornado6_Vuejs3_Edu
% d! F" O' }- M
# l2 z& S" M- c5 j1 `, ^" x