' i- P( R/ t# Y. w4 D: c3 w
当我们的用户厌倦了传统的电子邮件/密码注册流程时,他们会选择Google、GitHub等社交登录方式,这种方式虽然节约了用户的时间,但登录信息也会被第三方平台记录,也就是说我们用平台账号做了什么,平台都会一目了然,甚至还会对我们的行为进行分析、画像。那么有没有一种登录方式,它的所有信息都只保存在客户端和后端,并不牵扯三方平台授权,最大化的保证用户隐私呢?Web3.0给我们提供了一种选择:MetaMask。
MetaMask2 F$ z# W `9 y! s
MetaMask是用于与以太坊区块链进行交互的软件加密货币钱包。MetaMask允许用户通过浏览器插件或移动应用程序访问其以太坊钱包,然后可以使用这些扩展程序与去中心化应用程序进行交互。当然了,首先需要拥有一个MetaMask钱包,进入https://chrome.google.com/websto ... oehlefnkodbefgpgknn
安装metamask浏览器插件:' U7 p* ]- B4 s! ?( J% @
0 |" T: m3 ~' @9 x/ v0 k
随后点开插件,创建账号,记录密码、钱包地址、以及助记词等信息。
安装好插件之后,我们就可以利用这个插件和网站应用做交互了。! A* q* v$ D* l* u# ~9 d3 t5 m- i
* z, [9 k) i% D! A
钱包登录流程
登录逻辑和传统的三方登录还是有差异的,传统三方登录一般是首先跳转三方平台进行授权操作,随后三方平台将code验证码返回给登录平台,登录平台再使用code请求三方平台换取token,再通过token请求用户账号信息,而钱包登录则是先在前端通过Web3.js浏览器插件中保存的私钥对钱包地址进行签名操作,随后将签名和钱包地址发送到后端,后端利用Web3的库用同样的算法进行验签操作,如果验签通过,则将钱包信息存入token,并且返回给前端。
前端签名操作
首先需要下载前端的Web3.0操作库,https://docs.ethers.io/v4/,随后集成到登录页面中:
7 q( j" |1 j/ m) T( c8 f# `
- <script src="{{ static_url("js/ethers-v4.min.js") }}"></script># P N2 j0 O5 |3 e! V
- <script src="{{ static_url("js/axios.js") }}"></script>
- <script src="{{ static_url("js/vue.js") }}"></script>
; \, x E5 w' J3 Y3 T7 D/ z
这里我们基于Vue.js配合Axios使用。" b8 c8 i, f- t$ ]: _( b
接着声明登录激活方法:% h3 B$ D# ]: V" l1 K, K
4 t. K1 ^. ]4 T; V, _. `' [/ H
- sign_w3:function(){; I! ]* ]% Y- T$ q2 z6 _( j8 y1 q
- : \, z* k7 z* n4 z: E
- that = this;; A* i% J* ^4 U" W' H/ Q5 B
- ethereum.enable().then(function () {. X$ q' @3 x0 L5 @6 b7 Q* Q
- this.provider = new ethers.providers.Web3Provider(web3.currentProvider);
- / C5 u6 M6 ~( n5 |( s. u, v; Y
- this.provider.getNetwork().then(function (result) {5 `9 |+ N3 S V* Z) w, G
- if (result['chainId'] != 1) {
- 9 m9 m0 X+ n) s6 A
- console.log("Switch to Mainnet!")/ ^; _; l; Z) l- n. O" |) ^
- } else { // okay, confirmed we're on mainnet
- . } ?2 i+ [% k, h
- this.provider.listAccounts().then(function (result) {6 Z! i! ]0 p& d- o h
- console.log(result);
- this.accountAddress = result[0]; // figure out the user's Eth address
- this.provider.getBalance(String(result[0])).then(function (balance) {/ [7 T2 r. h5 A
- var myBalance = (balance / ethers.constants.WeiPerEther).toFixed(4);1 Y* V+ H4 L) M2 x$ S: \
- console.log("Your Balance: " + myBalance);4 w! }7 o e: r9 W6 G
- });
- // 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)3 V6 W6 e1 H. V; i T% c* z
- this.signer.signMessage("Signing in to "+document.domain+" at "+sortanow, accountAddress, "test password!")
- .then((signature) => { that.handleAuth(accountAddress,signature);- w( \3 f$ j8 Y t% c, H
- });
- . D. f- h2 ^: ?& V- l r
- console.log(this.signer);
- })/ l4 t& L A- q
- }
- })
- })
- },
y I0 [& ^$ R9 |* d, o1 Y+ p
- //检查验证
- handleAuth:function(accountAddress, signature){
- this.myaxios("/checkw3/","post",{"public_address":accountAddress,"signature":signature}).then(data =>{
- , J3 c+ y7 X9 c0 q1 s+ P# D
- if(data.errcode==0){! o3 H+ _" C- w% @ y. g9 ^( Q
- alert("欢迎:"+data.public_address);
- localStorage.setItem("token",data.token);
- localStorage.setItem("email",data.public_address);
- window.location.href = "/";& Q5 ~# e) p" j# }8 D
- }else{. w$ ]' o3 U% @; d5 s, k
- alert("验证失败");; D: E' ]3 C0 I
- }$ b7 [' A+ H/ K- _# L& P
- });
- 5 y( c- ]2 X1 k" ]
- . h& N; i! @+ L
- }
( t6 S. h' o& y- u2 K
' f: R3 l( A: |) s
/ }) q7 R4 S1 w ]7 d# {- x
随后创建异步视图方法:
- from tornado.web import url
- import tornado.web
- from tornado import httpclient/ |8 y2 ~3 v9 `) w( E5 s
- from .base import BaseHandler* [. C" P) @" j; x t! a/ d- f$ U9 K+ d
- from web3.auto import w3/ W% h- P; q4 D% A% {0 h" u
- from eth_account.messages import defunct_hash_message0 Q1 ^; z, b' F
- import time
- 7 J& l, H7 J" \- ?5 _
- class CheckW3(BaseHandler):4 U0 B' G. X2 C D2 `7 @
- B) c. E2 ~. p- Y/ l1 F
- async def post(self):: `1 I8 n( v: |/ S. d! x
- 2 z! W" k0 h# w5 `1 v8 L2 v! i
- public_address = self.get_argument("public_address")( Z9 p* [1 f# b( [) o$ C
- signature = self.get_argument("signature")
- domain = self.request.host
- if ":" in domain:0 h5 Y' K3 q C0 {$ N* ]
- domain = domain[0:domain.index(":")]
- now = int(time.time())( _8 x* d) F3 }: S4 C4 ~ ?+ W
- sortanow = now-now%600- f/ `$ m0 Z @" r. x
-
- original_message = 'Signing in to {} at {}'.format(domain,sortanow)
- print("[+] checking: "+original_message); x9 e1 T9 D1 Z7 e$ o; _8 U2 f
- message_hash = defunct_hash_message(text=original_message)
- signer = w3.eth.account.recoverHash(message_hash, signature=signature)
- 4 z, t2 O7 @: {. q* b7 w: C# W
- if signer == public_address:* s7 m( Q, C' o; c
- 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})8 x! O0 [7 r' _& l% |9 b
- self.finish({"msg":"ok","errcode":0,"public_address":public_address,"token":token})( I: ]9 D3 Q' }: t; ~
- else:
- self.finish({"msg":"could not authenticate signature","errcode":1})
这里通过recoverHash方法对签名进行反编译操作,如果反编译后的钱包地址和前端传过来的钱包地址吻合,那么说明当前账户的身份验证通过:
* s" m9 c: C# W6 b
( u" B/ i( n! k4 J7 {- c( y! G
# f0 ]' S' ?4 ~) O5 Z+ E* f
6 z) Z! ^. T0 b9 B5 i! b/ A
当验签通过之后,利用钱包地址在后台创建账号,随后将钱包地址、token等信息返回给前端,前端将其保存在stroage中即可。
结语
: E" E& d% @% {3 C- I: l
没错,将至已至,未来已来,是时候将Web3.0区块链技术融入产品了,虽然有些固有的思维方式依然在人们的脑海挥之不去,但世界却在时不我待地变化着,正是:青山遮不住,毕竟东流去!项目开源在https://github.com/zcxey2911/Tornado6_Vuejs3_Edu 1 t" e. K; q8 X! u. @4 {
/ `0 `4 U; P4 J3 Y