" t* d/ `8 V. ]9 o- I6 O" ^% k
当我们的用户厌倦了传统的电子邮件/密码注册流程时,他们会选择Google、GitHub等社交登录方式,这种方式虽然节约了用户的时间,但登录信息也会被第三方平台记录,也就是说我们用平台账号做了什么,平台都会一目了然,甚至还会对我们的行为进行分析、画像。那么有没有一种登录方式,它的所有信息都只保存在客户端和后端,并不牵扯三方平台授权,最大化的保证用户隐私呢?Web3.0给我们提供了一种选择:MetaMask。* x* c9 ^ `5 D3 c2 x% Y6 }
MetaMask
MetaMask是用于与以太坊区块链进行交互的软件加密货币钱包。MetaMask允许用户通过浏览器插件或移动应用程序访问其以太坊钱包,然后可以使用这些扩展程序与去中心化应用程序进行交互。当然了,首先需要拥有一个MetaMask钱包,进入https://chrome.google.com/websto ... oehlefnkodbefgpgknn $ W' D% Y6 ^ i
% t$ S( u0 f8 [) P1 O8 S
安装metamask浏览器插件: n" ~' k# n2 Y8 d2 f0 R" ]
* x6 p2 E- Y' I- A" Z8 \
随后点开插件,创建账号,记录密码、钱包地址、以及助记词等信息。
安装好插件之后,我们就可以利用这个插件和网站应用做交互了。
钱包登录流程
登录逻辑和传统的三方登录还是有差异的,传统三方登录一般是首先跳转三方平台进行授权操作,随后三方平台将code验证码返回给登录平台,登录平台再使用code请求三方平台换取token,再通过token请求用户账号信息,而钱包登录则是先在前端通过Web3.js浏览器插件中保存的私钥对钱包地址进行签名操作,随后将签名和钱包地址发送到后端,后端利用Web3的库用同样的算法进行验签操作,如果验签通过,则将钱包信息存入token,并且返回给前端。/ m3 u0 m+ q$ u H
前端签名操作6 G0 d3 C9 w }/ Z+ w4 m6 \
首先需要下载前端的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>
- ?' a; h3 P2 ]4 o
这里我们基于Vue.js配合Axios使用。% |6 I! N# j5 ]- k" M7 o3 ~6 y
$ c/ o& u) A9 t* t2 }3 i! c! l
接着声明登录激活方法:
% Y3 f9 X5 }$ V' F: M8 O {0 w. T
- sign_w3:function(){+ j6 O9 F3 l3 |0 R) t
- that = this;
- ethereum.enable().then(function () {- z( Z, W& s9 }5 W0 m
- this.provider = new ethers.providers.Web3Provider(web3.currentProvider);$ n; m4 b5 x7 O; f6 \! ~
- * n3 F# s7 d( j" y( b
- this.provider.getNetwork().then(function (result) {
- if (result['chainId'] != 1) {
- ; p0 f* [! Y* d# \0 X5 ]
- console.log("Switch to Mainnet!")
- } else { // okay, confirmed we're on mainnet
- this.provider.listAccounts().then(function (result) {( _* g* D6 K# ?0 h3 l7 V: Q
- 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);2 P, G" ?7 ?0 d5 J1 o+ S+ ]' a
- console.log("Your Balance: " + myBalance);5 Y2 c. I! T: h( F; z
- });
- : m3 A6 z5 X* h; [! E; V5 c+ t
- // get a signer object so we can do things that need signing/ S- ~9 w" @! ^$ N/ Z
- this.signer = provider.getSigner();
- var rightnow = (Date.now()/1000).toFixed(0)- s8 \, ]' W: S( G' z r* @9 M
- var sortanow = rightnow-(rightnow%600)8 b5 p; {3 e# w2 r0 o0 K7 f. F
- this.signer.signMessage("Signing in to "+document.domain+" at "+sortanow, accountAddress, "test password!")1 j3 F A1 k) g7 L
- .then((signature) => { that.handleAuth(accountAddress,signature);
- });8 m* Y i6 R! |! a# K, P: ^) D5 Z
- console.log(this.signer);/ ^8 M4 P) {/ j# p6 T3 l
- })
- }
- })
- })
- 7 T2 s3 B# H% w' a1 a z8 |
- },
- //检查验证
- handleAuth:function(accountAddress, signature){
- $ p8 Q! i, {' R' ~' r
- this.myaxios("/checkw3/","post",{"public_address":accountAddress,"signature":signature}).then(data =>{
- if(data.errcode==0){& A4 K8 {. N( W+ A9 @9 }9 Z6 A
- alert("欢迎:"+data.public_address);! K) s! Y% Y }. F
- localStorage.setItem("token",data.token);1 [2 o8 T2 k( x8 g
- localStorage.setItem("email",data.public_address);; A2 N) X$ [. z
- window.location.href = "/";. ~9 y7 k0 b! d* {- p# A3 z
- }else{
- alert("验证失败");
- }5 C# l/ q$ x6 i
- });
- 9 I8 o$ Z1 X" P5 f! U8 v
- }
; w9 @& \1 n8 v$ [5 ^# C5 ]
随后创建异步视图方法:7 c* V, A: Y% A: N6 w
- from tornado.web import url$ L3 N- s* B9 o; K$ I
- import tornado.web3 U0 a* P8 B7 F6 @' ~* r
- from tornado import httpclient
- from .base import BaseHandler% z- {; c9 S: b
- from web3.auto import w3
- from eth_account.messages import defunct_hash_message
- import time
- - O! H1 K! y' x! h* M6 _5 @
- class CheckW3(BaseHandler):
- async def post(self):
- public_address = self.get_argument("public_address"): D6 _( f$ B) f8 ~1 P
- signature = self.get_argument("signature")
- domain = self.request.host5 m5 R& f4 }" s8 J
- if ":" in domain:
- domain = domain[0:domain.index(":")]% c$ D G+ y1 w. o/ Q9 _6 u
- now = int(time.time())3 ^7 d; u/ V# S% H/ }
- sortanow = now-now%600
-
- original_message = 'Signing in to {} at {}'.format(domain,sortanow)9 c, [# N# B' L2 [; Z) Z: I1 i" A
- print("[+] checking: "+original_message)
- message_hash = defunct_hash_message(text=original_message)/ O& M3 [* C5 C% L1 [
- signer = w3.eth.account.recoverHash(message_hash, signature=signature)
- if signer == public_address:9 g: O6 E( J8 Q1 H2 H5 o4 m, ~
- try:
- user = await self.application.objects.get(User,email=public_address)
- except Exception as e:8 i; M% @% `1 Y* M, K- k
- user = await self.application.objects.create(User,email=public_address,password=create_password("third"),role=1)5 O$ s- [1 U8 @: r7 Y6 G
- myjwt = MyJwt()
- token = myjwt.encode({"id":user.id})6 q$ U/ }' l6 E3 `. k Z; ^
- self.finish({"msg":"ok","errcode":0,"public_address":public_address,"token":token})
- else:
- self.finish({"msg":"could not authenticate signature","errcode":1})
这里通过recoverHash方法对签名进行反编译操作,如果反编译后的钱包地址和前端传过来的钱包地址吻合,那么说明当前账户的身份验证通过:
8 r) H/ Y4 O! G) [! p& ?
当验签通过之后,利用钱包地址在后台创建账号,随后将钱包地址、token等信息返回给前端,前端将其保存在stroage中即可。; y3 q) u2 A! H8 C5 ~9 r5 v
2 [+ o# Q1 _ G: U1 L( f
结语5 `- ^/ l1 d3 G
没错,将至已至,未来已来,是时候将Web3.0区块链技术融入产品了,虽然有些固有的思维方式依然在人们的脑海挥之不去,但世界却在时不我待地变化着,正是:青山遮不住,毕竟东流去!项目开源在https://github.com/zcxey2911/Tornado6_Vuejs3_Edu - \% q4 { g: G9 ?* C: F1 U
% i( @ W' p& U$ o- d