内容简介:上一篇正如我们所说,我们的DApp是一个简单的HTML/ES6客户端。我们将在Asp.Net Core 2之上构建客户端,以利用使DApp成为
上一篇 用以太坊区块链保证Asp.Net Core的API安全 我们介绍了基本的解决方案,这一篇我们重点来看客户端。
正如我们所说,我们的DApp是一个简单的HTML/ES6客户端。我们将在Asp.Net Core 2之上构建客户端,以利用 IIS Express和Visual Studio IDE
。因此, Startup.cs
类中的 Configure
方法将是:
if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseDefaultFiles(); app.UseStaticFiles();
使DApp成为 NPM
项目并安装必备条件以使用ES6 Javascript标准。这不是强制性的,可以使用自己的堆栈构建DApp。
从项目文件夹运行 Powershell
并运行以下 NPM
命令:
npm init npm install webpack npm install babel-core babel-loader --save-dev npm install babel-preset-es2015 --save-dev npm install babel-preset-stage-0 --save-dev npm install babel-polyfill --save npm install babel-runtime --save npm install babel-plugin-transform-runtime --save-dev
要配置 webpack/babel
,请使用以下配置创建 webpack.config.js
文件:
var path = require("path"); module.exports = { entry: [ "babel-polyfill", "./src/main" ], output: { publicPath: "/js/", path: path.join(__dirname, "/wwwroot/js/"), filename: "main.build.js" } };
我们已设定 webpack
将 src/main.js
文件构建到 /www/js/main.build.js
。
安装以太坊扩展包:
npm install web3 npm install ethereumjs-util
Web3是一个 javascript
封装包,它简化了针对以太坊区块链的JSON RPC调用。 Ethereumjs-util
提供了一些以太坊特定的实用程序。让我们构建一个非常简单的HTML页面。我们需要一个登录按钮和另一个按钮来从我们的API层加载一些安全数据:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Ethereum Jwt Client</title> </head> <body> <h1>Ethereum Jwt Client</h1> <div id="login-view"> <label>Your account: </label> <span id="eth_account_span"></span> <button type="submit" id="login_btn">Login</button> </div> <div id="data-view"> <button type="submit" id="load_data_btn">Request secured data</button> <ul id="data_list"> </ul> </div> <script src="js/main.build.js"></script> </body> </html>
DApp逻辑将驻留在 src/main.js
文件中,正如我们在 webpack.config.js
文件中指定的那样。 src/main.js
文件将是:
let ethUtil = require('ethereumjs-util'); let Web3 = require('web3'); let coinbase = null; let accessToken = null; let init = () => { if (typeof web3 !== 'undefined') { web3 = new Web3(web3.currentProvider); web3.eth.getCoinbase(function (err, account) { if (err === null && ethUtil.isValidAddress(account)) { coinbase = account; eth_account_span.innerHTML = coinbase; } else { eth_account_span.innerHTML = 'Please unlock your account and refresh the page'; console.error(err); } }); } else { eth_account_span.innerHTML = 'Please install or unlock Metamask browser plugin or navigate this page with Mist or another web3 browser'; } }; let request = obj => { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open(obj.method || "GET", obj.url); if (obj.headers) { Object.keys(obj.headers).forEach(key => { xhr.setRequestHeader(key, obj.headers[key]); }); } xhr.onload = () => { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response); } else { reject(xhr.statusText); } }; xhr.onerror = () => reject(xhr.statusText); xhr.send(obj.body); }); }; login_btn.addEventListener('click', (e) => { e.preventDefault(); login_btn.setAttribute('disabled', 'disabled'); login_btn.innerHTML = 'Please sign the message'; let plain = 'Hi, you request a login from client to Eth Jwt Api. Please sign this message. This is not a transaction, is completely free and 100% secure. We\'ll use your signature to prove your ownership over your private key server side.'; let msg = ethUtil.bufferToHex(new Buffer(plain, 'utf8')); let hash = ethUtil.bufferToHex(ethUtil.keccak256("\x19Ethereum Signed Message:\n" + plain.length + plain)); let from = coinbase; let params = [msg, from]; let method = 'personal_sign'; web3.currentProvider.sendAsync({ method, params, from, }, function (err, result) { if (err || result.error) { login_btn.removeAttribute('disabled'); login_btn.innerHTML = 'Login'; console.error(err); return console.error(result.error); } console.log({ 'signature': result.result, 'msg': msg, 'hash': hash }); login_btn.innerHTML = 'Requesting token...'; let loginData = {}; loginData.signer = from; loginData.signature = result.result; loginData.message = msg; loginData.hash = hash; request({ url: 'http://localhost:49443/api/token', body: JSON.stringify(loginData), method: 'post', headers: { 'Authorization': 'Bearer ' + accessToken, 'Content-type': 'application/json' } }).then(data => { var json = JSON.parse(data); accessToken = json.token; console.log('access token: ' + accessToken); login_btn.removeAttribute('disabled'); login_btn.innerHTML = 'Login'; }).catch(error => { console.error(error); login_btn.removeAttribute('disabled'); login_btn.innerHTML = 'Login'; }); }); }); load_data_btn.addEventListener('click', (e) => { e.preventDefault(); request({ url: 'http://localhost:49443/api/values', headers: { 'Authorization': 'Bearer ' + accessToken } }).then(data => { var json = JSON.parse(data); for (let i = 0; i < json.length; i++) { data_list.innerHTML += '<li>' + json[i] + '</li>'; } }).catch(error => { console.error(err); }); }); window.addEventListener('load', init);
-
1.
coinbase
和accessToken
是全局变量,分别存储用户以太坊帐户和JWT token。 -
2.
init
函数从Metamask提供的提供程序初始化web3对象,然后它尝试检索用户的帐户(coinbase)。这需要解锁在Metamask中签名的帐户。 -
3.
require
函数只是hxr对象的封装,可以轻松地向API层调用ajax。 -
4. load_data_btn
单击处理程序对API层安全端点进行ajax调用。这需要有效的
accessToken
才能工作,否则,API层将响应401 HTTP响应。 - 5. login_btn 单击是一个两步功能。首先,它要求用户签署任意消息。签名后,它会将帐户,签名,明文消息和带前缀的哈希发送到令牌端点。
请注意, web3.personal.sign
将十六进制格式(0x …)的普通字符串的字节数组作为输入。
正如我们所说的,服务器端,我们将使用两种不同的方式从签名中恢复公钥:在一个中我们将使用JSON RPC 接口中的 web3.personal.ecrecover
(web3.personal.sign对应);在另一个中,我们将使用底层的 ecrecover
离线功能。根据文档, web3.personal.sign
使用底层签名函数来签署hash和前缀消息,因此,为了使用底层 ecrecover
对应,我们还需要计算并将此hash发送到令牌端点。
运行两个应用程序并使用安装了Metamask插件的浏览器导航到客户端。请记住,为了将 src/main.js
文件构建到 js/main.build.js
,你需要从 Powershell
运行 webpac
命令。如果一切正常,客户端将检索 coinbase
,你将在页面上看到你的帐户:
如果你现在单击“请求数据”按钮,将获得HTTP响应401。如果单击“登录”按钮,Metamask将提示你签名:
签名后,处理程序将对令牌端点进行ajax调用。在此阶段,身份验证方法不会检查任何签名,因此端点将始终发出JWT令牌。一旦收到JWT令牌,客户端就能通过ajax调用安全端点。如果现在单击“请求数据”按钮,将收到HTTP响应200和数据负载:
从签名中检索以太坊帐户
到目前为止, EthereumJwtApi
是一个简单的JWT Asp.Net核心示例,因为它不提供任何有效的身份验证方法。
TokenController
的关键部分是两个 Authenticate
方法及其从签名中检索以太坊帐户的能力。为此,你需要安装 Nethereum.Web3 NuGet
包。Nethereum是以太坊的.Net实现。
Authenticate
方法只是对 web3.personal.ecrecover
函数进行JSON RPC调用:
private async Task<UserVM> Authenticate(LoginVM login) { UserVM user = null; var client = new RpcClient(new Uri(_config["Nethereum:Geth"])); // Require the RPC endpoint of a Geth node as input eg: http://127.0.0.1:8545 var signer = await client.SendRequestAsync<string>(new RpcRequest(1, "personal_ecRecover", login.Message, login.Signature)); if (signer.ToLower().Equals(login.Signer.ToLower())) { // read user from DB or create a new one // for now we fake a new user user = new UserVM { Account = signer, Name = string.Empty, Email = string.Empty }; } return user; }
PRO:
web3.personal.sign
是 web3.personal.sign
的对应部分,因此你无需担心其底层实现。
缺点:
需要你自己的Geth节点。不支持Parity,Infura不允许JSON RPC调用 web3.personal.*
。 Authenticate2
方法显示了另一种方法,它使用底层 ecrecover
功能的离线实现:
private async Task<UserVM> Authenticate2(LoginVM login) { UserVM user = null; var signer = new Nethereum.Signer.MessageSigner(); var account = signer.EcRecover(login.Hash.HexToByteArray(), login.Signature); if (account.ToLower().Equals(login.Signer.ToLower())) { // read user from DB or create a new one // for now we fake a new user user = new UserVM { Account = account, Name = string.Empty, Email = string.Empty }; } return user; }
PRO:
不需要JSON RPC调用就能工作。 MessageSigner.EcRecover
是 Nethereum
提供的离线功能。
缺点:
你需要处理 web3.personal.sign
实现才能正确恢复帐户。出于这个原因,在客户端,我们相应地计算了前缀消息哈希。
结论
现在你拥有基本的知识和一个项目的骨架,可以使用以太坊保护你的Asp.Net Core 2 API。只需几点说明:
web3 1.0.0处于测试阶段, web3.personal.sign
实现可能会随着时间的推移而变化。请务必在你可以维护的代码库上使用这种身份验证方法。也许Infura某天决定允许 web3.personal.ecrecover
:-)
======================================================================
分享一些以太坊、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工程师不可多得的比特币开发学习课程。
汇智网原创翻译,转载请标明出处。这里是 原文
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 黑客频频攻击区块链安全软肋,如何保证区块链的安全?
- 用以太坊区块链保证Asp.Net Core的API安全(上)
- 如何保证代码质量?
- 可靠的因果保证
- Volatile不保证原子性(二)
- TCP协议如何保证可靠传输?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Effective C++
[美]Scott Meyers / 侯捷 / 电子工业出版社 / 2006-7 / 58.00元
《Effective C++:改善程序与设计的55个具体做法》(中文版)(第3版)一共组织55个准则,每一条准则描述一个编写出更好的C++的方式。每一个条款的背后都有具体范例支撑。第三版有一半以上的篇幅是崭新内容,包括讨论资源管理和模板(templates)运用的两个新章。为反映出现代设计考虑,对第二版论题做了广泛的修订,包括异常(exceptions)、设计模式(design patterns)......一起来看看 《Effective C++》 这本书的介绍吧!