y( o, d" F% P. @
当我们的用户厌倦了传统的电子邮件/密码注册流程时,他们会选择Google、GitHub等社交登录方式,这种方式虽然节约了用户的时间,但登录信息也会被第三方平台记录,也就是说我们用平台账号做了什么,平台都会一目了然,甚至还会对我们的行为进行分析、画像。那么有没有一种登录方式,它的所有信息都只保存在客户端和后端,并不牵扯三方平台授权,最大化的保证用户隐私呢?Web3.0给我们提供了一种选择:MetaMask。
MetaMask
MetaMask是用于与以太坊区块链进行交互的软件加密货币钱包。MetaMask允许用户通过浏览器插件或移动应用程序访问其以太坊钱包,然后可以使用这些扩展程序与去中心化应用程序进行交互。当然了,首先需要拥有一个MetaMask钱包,进入https://chrome.google.com/websto ... oehlefnkodbefgpgknn
安装metamask浏览器插件:
3 p: [% X" X7 m* X) E
随后点开插件,创建账号,记录密码、钱包地址、以及助记词等信息。) j4 Z2 l( O8 B- g: a: B9 `. g
安装好插件之后,我们就可以利用这个插件和网站应用做交互了。$ ?; u7 H* l7 g2 |. @' r
. k5 ]' U2 `: p, a+ i
钱包登录流程
登录逻辑和传统的三方登录还是有差异的,传统三方登录一般是首先跳转三方平台进行授权操作,随后三方平台将code验证码返回给登录平台,登录平台再使用code请求三方平台换取token,再通过token请求用户账号信息,而钱包登录则是先在前端通过Web3.js浏览器插件中保存的私钥对钱包地址进行签名操作,随后将签名和钱包地址发送到后端,后端利用Web3的库用同样的算法进行验签操作,如果验签通过,则将钱包信息存入token,并且返回给前端。7 Y# [7 Q4 C% ^. \0 B
# {( S: u5 \7 n0 S0 u! ]
3 b1 f, @! n. l. s$ o
前端签名操作: h* t- \. }5 I
首先需要下载前端的Web3.0操作库,https://docs.ethers.io/v4/,随后集成到登录页面中:
- <script src="{{ static_url("js/ethers-v4.min.js") }}"></script>1 b4 ]* R0 ^9 \' C x6 G' Y. K; a9 k
- <script src="{{ static_url("js/axios.js") }}"></script>7 ]" D! L0 g" q, y% g( ?
- <script src="{{ static_url("js/vue.js") }}"></script>
3 X- _: }, l- U0 V- {* Y
这里我们基于Vue.js配合Axios使用。9 q: |2 i( f- n! a: }
& W9 `* ?8 n }. A: i3 z/ f5 O
接着声明登录激活方法:
8 r0 x& p5 {, N! \" e
- sign_w3:function(){. w: t5 i8 V( ?! Z8 J2 C8 M, W: |
- that = this; u* q4 q- }! t4 X
- ethereum.enable().then(function () {/ ]6 R8 p% G+ P' ]% e6 e
- this.provider = new ethers.providers.Web3Provider(web3.currentProvider);6 e5 }- ?* Y6 F ?4 s7 E
- this.provider.getNetwork().then(function (result) {! G4 m7 A) @- g- [
- if (result['chainId'] != 1) { e7 k; L0 |5 u- ^6 C0 S
- console.log("Switch to Mainnet!")$ v5 G8 I6 Z8 r9 V# T8 w' P: y
- } else { // okay, confirmed we're on mainnet8 q: q+ ]) ~+ ~, H7 N
- this.provider.listAccounts().then(function (result) {. _1 u7 b, d3 M3 L) Z
- console.log(result);6 U% K! e5 A. a5 M4 M! S% O
- 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);3 H; k( K6 O, q
- console.log("Your Balance: " + myBalance);/ s: d. b. S$ b* e$ W
- });9 t- C; K; v' M6 `$ z( m" a3 o
- // get a signer object so we can do things that need signing
- this.signer = provider.getSigner();
- & ^& s. E" g; t: g* f2 U) W8 S
- var rightnow = (Date.now()/1000).toFixed(0)
- var sortanow = rightnow-(rightnow%600)7 \( R; ?$ v1 Q! l+ C
- 0 }4 z% ~& C! @' `" c9 W, l+ E! @1 @2 ^
- this.signer.signMessage("Signing in to "+document.domain+" at "+sortanow, accountAddress, "test password!")/ L& b# s3 f1 F: w: t& M
- .then((signature) => { that.handleAuth(accountAddress,signature);
- });
- 3 x: P* {2 t7 j) W
- console.log(this.signer);
- })
- }
- })
- })! Q* D4 W6 X6 Q7 j+ A$ ]
- . A1 _( u; e! j( T! L/ `$ z
- },
- //检查验证
- handleAuth:function(accountAddress, signature){4 |3 Q% j7 Y0 \# c
- " t- b% W( K/ D
- this.myaxios("/checkw3/","post",{"public_address":accountAddress,"signature":signature}).then(data =>{
- " D. _, b" t. G5 i
- if(data.errcode==0){
- alert("欢迎:"+data.public_address);7 u. k& W; ~, v8 |# e
- localStorage.setItem("token",data.token);* l( s, V& R7 Q/ ?
- localStorage.setItem("email",data.public_address);
- window.location.href = "/";
- }else{
- alert("验证失败");
- }5 c% z Z3 q w
- });
- 2 O8 ~9 f: K) I! F; j% ^" _
- }
# c: e, h+ l$ p1 o
. D' J7 h& F- Z
随后创建异步视图方法:
- from tornado.web import url( i, \" k& _* V( O/ R3 a/ q
- import tornado.web
- from tornado import httpclient
- from .base import BaseHandler( X4 `. {6 w9 k* ^2 z
- from web3.auto import w30 s! q+ L$ ~/ R, L" K* l0 `
- from eth_account.messages import defunct_hash_message
- import time" k# k6 Z2 h/ @# H4 x6 ^- k4 \9 [
- $ ]+ m, g! _5 O9 T1 ]! P
- class CheckW3(BaseHandler):
- async def post(self):" ^4 S* Y1 t* a8 x0 h9 R
- 1 b7 ?# C. _5 F u. K
- public_address = self.get_argument("public_address")
- signature = self.get_argument("signature"); m" d+ N0 \0 ?/ r7 I
- domain = self.request.host7 Y0 ?6 G& z! y* {
- if ":" in domain:
- domain = domain[0:domain.index(":")]4 d8 r ^# V; |* R' z
- now = int(time.time())
- sortanow = now-now%600$ I& T) ^8 e/ |
- 9 r' j" w' R3 w& ^
- original_message = 'Signing in to {} at {}'.format(domain,sortanow)
- print("[+] checking: "+original_message)
- message_hash = defunct_hash_message(text=original_message)" U) `# T: {9 S2 p- ?4 [4 Q
- signer = w3.eth.account.recoverHash(message_hash, signature=signature)0 P( M. a2 T) g; N* Q2 Y
- ' s& x; ?% S! P4 N, c
- if signer == public_address:
- try:9 ]: f5 O) ]$ E0 u5 {, D* E# q
- 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)
- , o7 g& ~- z g$ j
- myjwt = MyJwt()
- token = myjwt.encode({"id":user.id})
- self.finish({"msg":"ok","errcode":0,"public_address":public_address,"token":token})
- else:9 X, n# Z- T( t9 w4 T3 `
- self.finish({"msg":"could not authenticate signature","errcode":1})
这里通过recoverHash方法对签名进行反编译操作,如果反编译后的钱包地址和前端传过来的钱包地址吻合,那么说明当前账户的身份验证通过:4 H& h, {& r3 O5 O. X" u
% G5 F0 d* G/ ^. R. ~' d+ j6 ~' E
7 A& x3 Z t% {5 j4 w! ~2 o
当验签通过之后,利用钱包地址在后台创建账号,随后将钱包地址、token等信息返回给前端,前端将其保存在stroage中即可。( [/ z- s: m6 P
( T2 ` ^! r5 U
结语" W& p3 n; T3 U4 Z0 z9 A* ]
. B; Q' K2 b E6 p" J
没错,将至已至,未来已来,是时候将Web3.0区块链技术融入产品了,虽然有些固有的思维方式依然在人们的脑海挥之不去,但世界却在时不我待地变化着,正是:青山遮不住,毕竟东流去!项目开源在https://github.com/zcxey2911/Tornado6_Vuejs3_Edu
! @6 }4 }! J( w
2 e9 v! O# O" j% ~# R7 D K