以太坊和Metamask開發web應用時不再需要密碼

weixin_33763244發表於2018-11-27

我在ConsenSys為各種客戶構建了大量的概念證明,通常他們想要利用以太坊區塊鏈來解決某些業務用例。奇怪的是,這些系統通常設計有標準的網路登入(即使用者名稱和密碼)。我總是問自己為什麼我還在這樣做設計,畢竟,這是今天乙太網目前可以解決每個煩人的Web應用程式的一個方面。所以我決定停下腳步,設計一下這個解決方案。

JSON Web token

登入標準Web系統(和/或使用其API)的一種非常流行的方法是將密碼(經過雜湊的客戶端)提交給認證端點並接收token作為回報。這通常稱為JSON Web Token,通常在一段有限的時間內(幾分鐘到幾天)有效。這是一個關於標準實現的很好的教程。

JSON Web Token很好,我開始認為在區塊鏈上驗證自己很容易。事實上,當你使用以太坊時,你需要不斷地去改進。

如果你將乙太網地址(這只是公鑰的sha3雜湊)視為網站上的帳戶,則可以通過使用私鑰對一段資料進行簽名來證明你擁有該帳戶,這非常容易。此資料是任意的,可以是網站API提供的任意隨機字串。因此,我們可以使用地址作為使用者名稱並繞過密碼的需要。事實上,我們甚至不需要使用區塊鏈來做到這一點。

這是使用Express的樣子:

首先,我們需要使用私鑰進行橢圓曲線簽名:

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

不要過分擔心這些引數是什麼。這裡有一些密碼學,我鼓勵你閱讀橢圓曲線簽名。比特幣維基是一個不錯的起點。

無論如何,一旦我們有了簽名元件,我們就可以將它們與使用者的地址一起打包並將其全部傳送到認證端點。

POST/Authenticate

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);
  ...
}

我們終於搞定它了! 你的使用者已經完全登入,但不需要密碼。

UI方面

使用者如何在瀏覽器中實際簽署此資料?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,你就可以呼叫經過身份驗證的端點。我們終於得到它了!

請注意,必須從簽名中恢復vrs值。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時使用密碼

相關文章