我在ConsenSys为各类客户构建了大量的概念证实,一般他们想要利用以太坊区块链来解决某些业务用例。奇怪的是,这些系统一般设计有标准的网络登陆(即用户名和密码)。我老是问本身为何我还在这样作设计,毕竟,这是今天以太网目前能够解决每一个烦人的Web应用程序的一个方面。因此我决定停下脚步,设计一下这个解决方案。php
登陆标准Web系统(和/或使用其API)的一种很是流行的方法是将密码(通过哈希的客户端)提交给认证端点并接收token做为回报。这一般称为JSON Web Token,一般在一段有限的时间内(几分钟到几天)有效。这是一个关于标准实现的很好的教程。java
JSON Web Token很好,我开始认为在区块链上验证本身很容易。事实上,当你使用以太坊时,你须要不断地去改进。node
若是你将以太网地址(这只是公钥的sha3哈希)视为网站上的账户,则能够经过使用私钥对一段数据进行签名来证实你拥有该账户,这很是容易。此数据是任意的,能够是网站API提供的任意随机字符串。所以,咱们可使用地址做为用户名并绕过密码的须要。事实上,咱们甚至不须要使用区块链来作到这一点。python
这是使用Express的样子:android
首先,咱们须要使用私钥进行椭圆曲线签名:git
var ethUtil = require(‘ethereumjs-util’); // >=5.1.1 var data = ‘i am a string’; // Elliptic curve signature must be done on the Keccak256 Sha3 hash of a piece of data. var message = ethUtil.toBuffer(data); var msgHash = ethUtil.hashPersonalMessage(message); var sig = ethUtil.ecsign(msgHash, privateKey); var serialized = ethUtil.bufferToHex(this.concatSig(sig.v, sig.r, sig.s)) return serialized
不要过度担忧这些参数是什么。这里有一些密码学,我鼓励你阅读椭圆曲线签名。比特币维基是一个不错的起点。程序员
不管如何,一旦咱们有了签名组件,咱们就能够将它们与用户的地址一块儿打包并将其所有发送到认证端点。github
POST/Authenticateweb
var jwt = require(‘jsonwebtoken’); var ethUtil = require('ethereumjs-util'); function checkSig(req, res) { var sig = req.sig; var owner = req.owner; // Same data as before var data = ‘i am a string’; var message = ethUtil.toBuffer(data) var msgHash = ethUtil.hashPersonalMessage(message) // Get the address of whoever signed this message var signature = ethUtil.toBuffer(sig) var sigParams = ethUtil.fromRpcSig(signature) var publicKey = ethUtil.ecrecover(msgHash, sigParams.v, sigParams.r, sigParams.s) var sender = ethUtil.publicToAddress(publicKey) var addr = ethUtil.bufferToHex(sender) // Determine if it is the same address as 'owner' var match = false; if (addr == owner) { match = true; } if (match) { // If the signature matches the owner supplied, create a // JSON web token for the owner that expires in 24 hours. var token = jwt.sign({user: req.body.addr}, ‘i am another string’, { expiresIn: “1d” }); res.send(200, { success: 1, token: token }) } else { // If the signature doesn’t match, error out res.send(500, { err: ‘Signature did not match.’}); } }
因此基本上,给定一些数据,一个地址和一个EC签名的组件,咱们能够安全的证实该地址属于签署数据的人。很酷,对吧?算法
一旦咱们对签名和地址匹配感到满意,咱们就能够为该地址服务器端签署一个JSON Web token。 在这种状况下,token有效期为1天。
如今咱们只须要放入一些中间件来保护任何服务或修改受保护信息的路由。
middleware/auth.js
function auth(req, res, next) { jwt.verify(req.body.token, ‘i am another string’, function(err, decoded) { if (err) { res.send(500, { error: ‘Failed to authenticate token.’}); } else { req.user = decoded.user; next(); }; }); }
app.js
// Routes app.post(‘/UpdateData’, auth, Routes.UpdateData); …
若是提供的Token对应于发送请求的用户,咱们将继续请求路由。请注意,中间件会修改请求。咱们须要引用这个新的user
参数,由于咱们知道它已经在咱们的中间件中设置了。
POST/UpdateData
function UpdateData(req, res) { // Only use the user that was set in req by auth middleware! var user = req.user; updateYourData(user, req.body.data); ... }
咱们终于搞定它了! 你的用户已经彻底登陆,但不须要密码。
用户如何在浏览器中实际签署此数据?Metamask会提供帮助!Metamask是一个整洁的chrome扩展,它将web3注入你的浏览器窗口。
mycomponent.jsx
makeSig(dispatch) { function toHex(s) { var hex = ‘’; for(var i=0;i<s.length;i++) { hex += ‘’+s.charCodeAt(i).toString(16); } return `0x${hex}`; } var data = toHex(‘i am a string’); web3.currentProvider.sendAsync({ id: 1, method: 'personal_sign', params: [web3.eth.accounts[0], data] }, function(err, result) { let sig = result.result; dispatch(exchange.authenticate(sig, user)) }) } } render(){ let { dispatch, _main: { sig } } = this.props; if (Object.keys(sig).length == 0) { this.makeSig(dispatch); } return ( <p>I am a webpage</p> ); }
这将触发Metamask弹出一个窗口,要求用户对消息进行签名:
一旦调用了回调,它将调用如下操做:
authenticate(sig, user) { return (dispatch) => { fetch(`${this.api}/Authenticate`, { method: 'POST', body: JSON.stringify({ owner: user, sig: sig}), headers: { "Content-Type": "application/json" } }) .then((res) => { return res.text(); }) .then((body) => { var token = JSON.parse(body).token; dispatch({ type: 'SET_AUTH_TOKEN', result: token}) }) } }
一旦你在reducer中保存了auth token,你就能够调用通过身份验证的端点。咱们终于获得它了!
请注意,必须从签名中恢复v
,r
和s
值。Metamask有一个签名util模块,用于显示签名的构造方式。它能够像这样解构:
var solidity_sha3 = require('solidity-sha3').default; let hash = solidity_sha3(data); let sig = result.result.substr(2, result.result.length); let r = sig.substr(0, 64); let s = sig.substr(64, 64); let v = parseInt(sig.substr(128, 2));
其中r
将被解析为0或1.另请注意,这使用solidity-sha3模块来确保此哈希算法与用做solidity本机hash方法的哈希算法相同(咱们正在hash以前签名的十六进制字符串))。
我没法强调使用JSON Web token的每一个Web应用程序今天均可以轻松利用这一点。具备Metamask扩展的任何用户均可以简单地绕过登陆屏幕,其安全性可能比目前用于管理登陆的任何内容都要好。这意味着更少的忘记密码,更少的浪费时间和更快乐的用户群。
并且,你知道,若是你但愿你的用户在没有中间人的状况下向对方(或你或使用此用户的任何其余系统上的用户)付款,或者若是你想要利用以太坊的其余百万其余功能,那么你须要也这样作。
今天开始,加入咱们以太坊,去征服世界。
======================================================================
分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:
- java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
- python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
- php以太坊,主要是介绍使用php进行智能合约开发交互,进行帐号建立、交易、转帐、代币开发以及过滤器和交易等内容。
- 以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
- 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
- C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括帐户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
- EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、帐户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
- java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如建立地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
- php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如建立地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
- tendermint区块链开发详解,本课程适合但愿使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。
汇智网原创翻译,转载请标明出处。这里是原文不要再在以太坊和Metamask开发web时使用密码