( x- W; @% x$ }0 i3 V; \/ C+ U+ P% Y
当我们的用户厌倦了传统的电子邮件/密码注册流程时,他们会选择Google、GitHub等社交登录方式,这种方式虽然节约了用户的时间,但登录信息也会被第三方平台记录,也就是说我们用平台账号做了什么,平台都会一目了然,甚至还会对我们的行为进行分析、画像。那么有没有一种登录方式,它的所有信息都只保存在客户端和后端,并不牵扯三方平台授权,最大化的保证用户隐私呢?Web3.0给我们提供了一种选择:MetaMask。
) x" d# U. M- D0 M# X1 U; A
MetaMask
MetaMask是用于与以太坊区块链进行交互的软件加密货币钱包。MetaMask允许用户通过浏览器插件或移动应用程序访问其以太坊钱包,然后可以使用这些扩展程序与去中心化应用程序进行交互。当然了,首先需要拥有一个MetaMask钱包,进入https://chrome.google.com/websto ... oehlefnkodbefgpgknn
! W `; A' L L0 @0 \
安装metamask浏览器插件:1 {. Q& T8 f2 I c# _2 X+ X
- a* L: @) o' t2 p% E
随后点开插件,创建账号,记录密码、钱包地址、以及助记词等信息。' b* T( ^: `& v, N
安装好插件之后,我们就可以利用这个插件和网站应用做交互了。7 H+ C- Z" B, m# Y
钱包登录流程
登录逻辑和传统的三方登录还是有差异的,传统三方登录一般是首先跳转三方平台进行授权操作,随后三方平台将code验证码返回给登录平台,登录平台再使用code请求三方平台换取token,再通过token请求用户账号信息,而钱包登录则是先在前端通过Web3.js浏览器插件中保存的私钥对钱包地址进行签名操作,随后将签名和钱包地址发送到后端,后端利用Web3的库用同样的算法进行验签操作,如果验签通过,则将钱包信息存入token,并且返回给前端。2 `; y- X+ P0 _
, d g- E0 q" v o, c0 A
3 j/ c* P: O. k# ?6 f9 }0 P
前端签名操作
首先需要下载前端的Web3.0操作库,https://docs.ethers.io/v4/,随后集成到登录页面中:
( U r- U6 J; a$ h6 w& J/ n- i
- <script src="{{ static_url("js/ethers-v4.min.js") }}"></script>7 s2 w* q6 Q1 z8 D1 @0 J* V
- <script src="{{ static_url("js/axios.js") }}"></script>
- <script src="{{ static_url("js/vue.js") }}"></script>
" S- ? ^0 y0 N8 C) ]: b
这里我们基于Vue.js配合Axios使用。; K O4 D; J [ x. J
接着声明登录激活方法:
- sign_w3:function(){. T+ \+ k% p4 T
- that = this;
- ethereum.enable().then(function () {/ Z0 P' ^/ I, Z& }
- this.provider = new ethers.providers.Web3Provider(web3.currentProvider);6 u; q! O* I- E6 ]0 G1 U8 L7 E
- ! J' \& R6 R/ ^5 _: }$ q: G
- this.provider.getNetwork().then(function (result) {
- if (result['chainId'] != 1) {# X" b5 y! {2 w% ]
- console.log("Switch to Mainnet!")" K! y9 R; [4 f( C: \8 q( u" z
- } else { // okay, confirmed we're on mainnet
- ' [* G6 i* i' x& B# B
- this.provider.listAccounts().then(function (result) {$ d m. U; z- W6 C) F
- console.log(result);
- this.accountAddress = result[0]; // figure out the user's Eth address
- this.provider.getBalance(String(result[0])).then(function (balance) {( v- L$ g- a% F
- var myBalance = (balance / ethers.constants.WeiPerEther).toFixed(4);
- console.log("Your Balance: " + myBalance);: g* K9 ]8 t: G7 [0 D. g$ E
- });
- % B: v `3 X/ V, E
- // get a signer object so we can do things that need signing
- this.signer = provider.getSigner(); p. Q! b5 d8 H7 q/ h+ t
- y# B7 W0 E8 q; L
- var rightnow = (Date.now()/1000).toFixed(0)$ z$ U w# t b$ x) [, u: P
- var sortanow = rightnow-(rightnow%600)8 a, T( Y% q4 C% o7 U6 j
- & Q5 W6 u9 x, D# C! ?3 q' J
- this.signer.signMessage("Signing in to "+document.domain+" at "+sortanow, accountAddress, "test password!")
- .then((signature) => { that.handleAuth(accountAddress,signature);1 U7 o" b# k2 j( d* }/ f
- });6 Q0 K1 C# p3 U0 i: h1 V. S
- % S1 w3 j$ ~$ T) g) z" X
- console.log(this.signer);& ^) \1 K, u4 ?* Q
- })
- }
- })
- })
- 8 i# C- h2 g' e* x
- },
- //检查验证5 A) q) w2 s9 e0 F! N$ \/ H, W( L
- handleAuth:function(accountAddress, signature){6 ^7 E3 g3 ~" M, |, j' r' I
- ' c) N7 Q2 o3 R
- . y0 @: }5 ]! I# x! H" k8 x
- this.myaxios("/checkw3/","post",{"public_address":accountAddress,"signature":signature}).then(data =>{
- * y1 f. L$ R; A1 f6 g# f
- if(data.errcode==0){
- alert("欢迎:"+data.public_address);
- localStorage.setItem("token",data.token);* D" n/ S3 R& X. O4 [- O
- localStorage.setItem("email",data.public_address);" l! \6 z; X0 N: V0 L* ?; Z, Q0 J# E
- window.location.href = "/";& I( }, {; a" y. E" @' j/ [- v) [
- }else{# r: k! U' @" t8 Q1 X" g
- alert("验证失败");
- }
- });/ I M5 |3 l* B
- ! ^) T! R# i; q$ l
- }
; F7 h+ M# a. g! w V
* ^1 A0 I# E' C' ?2 K4 a0 v3 g4 h
随后创建异步视图方法:1 |" p! |" }% K
- from tornado.web import url9 G/ ~; m1 r2 o* l
- import tornado.web0 A, n) s! o+ B! v$ }
- from tornado import httpclient
- from .base import BaseHandler
- from web3.auto import w3( G! D! t4 ~+ K; M: |, L
- from eth_account.messages import defunct_hash_message, Z+ d; _) \' S. O$ q' H3 q
- import time+ ], x9 y+ h0 [6 q4 G
- class CheckW3(BaseHandler):
- ) w$ s+ y" T9 t9 g% [
- async def post(self):. k7 g" X! P7 j( u
- public_address = self.get_argument("public_address")# q" h$ h8 b5 Y+ D3 c$ @
- signature = self.get_argument("signature")5 E# }0 ]) L* b4 M9 N$ O3 ?$ \
- . G- B/ |' W! I1 T t/ E3 z
- domain = self.request.host% J" Q, Q% L0 M; a) J" J5 l: T
- if ":" in domain:! T6 r$ H8 G/ y* m) E
- domain = domain[0:domain.index(":")]
- . [5 c- ]! p2 C0 C# ?1 Q5 _
- now = int(time.time())
- sortanow = now-now%600
- s, K1 b1 j @. k$ c+ W8 q
- original_message = 'Signing in to {} at {}'.format(domain,sortanow)% S; ~! H+ \- b6 l5 D4 ^+ `) q
- print("[+] checking: "+original_message)
- message_hash = defunct_hash_message(text=original_message)
- signer = w3.eth.account.recoverHash(message_hash, signature=signature)- P9 r4 Z( f! |, G3 d i0 c6 I
- if signer == public_address:
- try:% n( I9 ?* `2 o R2 o
- 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)7 R% K* u, x# ] H7 D! Q
- myjwt = MyJwt()
- token = myjwt.encode({"id":user.id})! B5 f8 u/ O, x
- self.finish({"msg":"ok","errcode":0,"public_address":public_address,"token":token})! }# g1 ?" h2 N
- else:3 `; f8 N7 J0 w3 R1 o- ~
- self.finish({"msg":"could not authenticate signature","errcode":1})
这里通过recoverHash方法对签名进行反编译操作,如果反编译后的钱包地址和前端传过来的钱包地址吻合,那么说明当前账户的身份验证通过:7 }( O4 ^$ F/ v
1 v) R6 [0 t7 z+ f4 T2 _
3 B e& Z" n: ^' n# h
当验签通过之后,利用钱包地址在后台创建账号,随后将钱包地址、token等信息返回给前端,前端将其保存在stroage中即可。
结语0 |! u, C) u( k/ H' }
) H0 F6 ]8 }0 {7 m5 @
没错,将至已至,未来已来,是时候将Web3.0区块链技术融入产品了,虽然有些固有的思维方式依然在人们的脑海挥之不去,但世界却在时不我待地变化着,正是:青山遮不住,毕竟东流去!项目开源在https://github.com/zcxey2911/Tornado6_Vuejs3_Edu 9 s' E4 s: ?: o) r7 C
4 q7 O) a- m; C, m8 x