我在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時使用密碼