当我们的用户厌倦了传统的电子邮件/密码注册流程时,他们会选择Google、GitHub等社交登录方式,这种方式虽然节约了用户的时间,但登录信息也会被第三方平台记录,也就是说我们用平台账号做了什么,平台都会一目了然,甚至还会对我们的行为进行分析、画像。那么有没有一种登录方式,它的所有信息都只保存在客户端和后端,并不牵扯三方平台授权,最大化的保证用户隐私呢?Web3.0给我们提供了一种选择:MetaMask。
MetaMask1 W' y' E& ]4 {8 z
MetaMask是用于与以太坊区块链进行交互的软件加密货币钱包。MetaMask允许用户通过浏览器插件或移动应用程序访问其以太坊钱包,然后可以使用这些扩展程序与去中心化应用程序进行交互。当然了,首先需要拥有一个MetaMask钱包,进入https://chrome.google.com/websto ... oehlefnkodbefgpgknn
& G' l( a$ g: x! q0 z( ?' m
安装metamask浏览器插件:
2 O7 n5 i/ S/ T2 d7 Y
4 k7 p/ w6 p/ Z" b( |$ J$ R
随后点开插件,创建账号,记录密码、钱包地址、以及助记词等信息。0 e$ t/ _. s1 _7 \
安装好插件之后,我们就可以利用这个插件和网站应用做交互了。
钱包登录流程
登录逻辑和传统的三方登录还是有差异的,传统三方登录一般是首先跳转三方平台进行授权操作,随后三方平台将code验证码返回给登录平台,登录平台再使用code请求三方平台换取token,再通过token请求用户账号信息,而钱包登录则是先在前端通过Web3.js浏览器插件中保存的私钥对钱包地址进行签名操作,随后将签名和钱包地址发送到后端,后端利用Web3的库用同样的算法进行验签操作,如果验签通过,则将钱包信息存入token,并且返回给前端。: y, {; N: j8 ?3 ]" k/ N4 z: i, C
- a! R# M9 r7 g
前端签名操作) D" Q3 G) |# s
首先需要下载前端的Web3.0操作库,https://docs.ethers.io/v4/,随后集成到登录页面中:' X3 b# Q$ D- m& V+ G* I* Z" h) |
3 Q) C" a( Y2 A' j2 z$ T
- <script src="{{ static_url("js/ethers-v4.min.js") }}"></script>( Z& @( j4 y, L }1 @
- <script src="{{ static_url("js/axios.js") }}"></script>8 J: c& ~' \$ w4 _9 E- F
- <script src="{{ static_url("js/vue.js") }}"></script>
7 K$ q( n1 T. \& `8 ^$ `
这里我们基于Vue.js配合Axios使用。
接着声明登录激活方法:
- sign_w3:function(){% [: o3 E ~: `
- that = this;
- ethereum.enable().then(function () {
- h2 o8 T: X4 B
- this.provider = new ethers.providers.Web3Provider(web3.currentProvider);
- * k6 ] z) M/ W1 D; n& D% _
- this.provider.getNetwork().then(function (result) {
- if (result['chainId'] != 1) {
- # j p5 u. v |+ w2 Q& }
- console.log("Switch to Mainnet!")
- 7 S/ u" Z) W, m1 u7 j" T
- } else { // okay, confirmed we're on mainnet
- this.provider.listAccounts().then(function (result) {7 |& R7 w+ ?2 k, k+ |" A6 x
- console.log(result);
- this.accountAddress = result[0]; // figure out the user's Eth address; S3 P: u+ t& |/ l/ p
- this.provider.getBalance(String(result[0])).then(function (balance) {) N( t8 A' {) K
- var myBalance = (balance / ethers.constants.WeiPerEther).toFixed(4); S" D# Y: z O
- console.log("Your Balance: " + myBalance);- q) |8 f! N' T. |! m
- });6 P" R0 {( u x% U) E
- // get a signer object so we can do things that need signing
- this.signer = provider.getSigner();
- * U8 a# d. x! ] ~7 ]8 c0 v2 x
- var rightnow = (Date.now()/1000).toFixed(0)
- var sortanow = rightnow-(rightnow%600), D( N: N ?. E/ q
- % v i7 S1 ^+ u
- this.signer.signMessage("Signing in to "+document.domain+" at "+sortanow, accountAddress, "test password!")
- .then((signature) => { that.handleAuth(accountAddress,signature);
- });/ I# K: v" D2 \1 U! O2 p5 R
- console.log(this.signer);) Y Q, n! i4 {/ q: Q j, w. \
- })5 v; }% q, g5 O/ D1 E! N& F
- }1 V: W( o, Q( e' }/ B
- }); k4 M& ?9 L4 Y6 ?, l+ @) d! o
- })1 j3 P* D5 s& q q/ [
- 7 ~) g/ {) u& j8 q$ ~7 n4 e( L
- },
* B) n9 u% I6 l8 o4 @$ d' D$ T' G0 b5 [
- //检查验证
- handleAuth:function(accountAddress, signature){- Y" t0 p5 [( I$ e: y
- 6 Q! c$ k9 ]7 M- d! ?6 y
- this.myaxios("/checkw3/","post",{"public_address":accountAddress,"signature":signature}).then(data =>{
- if(data.errcode==0){2 u9 ^+ u& x' [" B r3 h
- alert("欢迎:"+data.public_address);
- localStorage.setItem("token",data.token);
- localStorage.setItem("email",data.public_address);. f% s* S+ M! P0 \$ _) V" e
- window.location.href = "/";
- }else{
- alert("验证失败");0 O- l2 R1 T, i( I( J
- }
- }); Y, i) P2 c$ w
- . k; [3 T5 I6 z3 t- ?
- ) E4 O3 z7 B4 I$ i% v
- }
. I- x2 i% ~# E/ E1 V7 X5 s
随后创建异步视图方法:2 C: C. a" a" m+ d
- from tornado.web import url& F5 ]: v0 \1 H4 Z4 n5 N+ S5 D+ u
- import tornado.web
- from tornado import httpclient6 ~- X6 [8 z! ?# T
- from .base import BaseHandler
- from web3.auto import w35 O6 Z( \& e" H6 V/ Z+ p/ B/ A
- from eth_account.messages import defunct_hash_message$ A8 L" z1 k" i' U
- import time
- 5 `4 a/ j2 D6 Q3 [
- class CheckW3(BaseHandler):2 N% \3 o, ?0 o
- async def post(self):
- " J2 }' g- l/ t% M |8 {
- public_address = self.get_argument("public_address")
- signature = self.get_argument("signature")
- ; {, @6 n5 X3 d
- domain = self.request.host
- if ":" in domain:. ]4 J6 \6 B# a3 ]$ {8 u3 ^
- domain = domain[0:domain.index(":")]
- now = int(time.time())' g7 O( X. X2 }9 K! O+ T; K# \
- sortanow = now-now%600
-
- original_message = 'Signing in to {} at {}'.format(domain,sortanow) w8 ?4 ^7 L: B# B
- print("[+] checking: "+original_message)
- message_hash = defunct_hash_message(text=original_message)9 k- \4 A- O; p2 C. I4 f
- signer = w3.eth.account.recoverHash(message_hash, signature=signature)0 i9 N* q$ Q- ?: `$ k% o
- if signer == public_address:+ u# y3 W# i& M! N
- try:, |) C r Q( Z) s# D; i* W
- user = await self.application.objects.get(User,email=public_address). |3 I9 A% w" N
- except Exception as e: |7 X J5 B3 ` H6 j0 h
- user = await self.application.objects.create(User,email=public_address,password=create_password("third"),role=1)
- 6 l( } J5 ]2 K
- myjwt = MyJwt()! n( e* a1 x& Z4 O* e7 [
- token = myjwt.encode({"id":user.id})
- self.finish({"msg":"ok","errcode":0,"public_address":public_address,"token":token})
- else:
- self.finish({"msg":"could not authenticate signature","errcode":1})
这里通过recoverHash方法对签名进行反编译操作,如果反编译后的钱包地址和前端传过来的钱包地址吻合,那么说明当前账户的身份验证通过:7 [2 n) q/ @( e4 |, T5 h. l
$ Q9 Y* S' r2 J& `. G& d
* D. K- ~, t/ R! ]" [
) P+ ?# S3 P/ x; e" y* U$ e% e
当验签通过之后,利用钱包地址在后台创建账号,随后将钱包地址、token等信息返回给前端,前端将其保存在stroage中即可。8 `0 r' r- ], m0 D4 ]8 Q
结语, d+ \* |; Q2 I
7 ^( a7 P3 l% [. j! F1 F3 [
没错,将至已至,未来已来,是时候将Web3.0区块链技术融入产品了,虽然有些固有的思维方式依然在人们的脑海挥之不去,但世界却在时不我待地变化着,正是:青山遮不住,毕竟东流去!项目开源在https://github.com/zcxey2911/Tornado6_Vuejs3_Edu
- f4 K& ~/ L( O