用以太坊区块链保证Asp.Net Core的API安全(下)

栏目: ASP.NET · 发布时间: 6年前

内容简介:上一篇正如我们所说,我们的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"
    }
};

我们已设定 webpacksrc/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. coinbaseaccessToken 是全局变量,分别存储用户以太坊帐户和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 ,你将在页面上看到你的帐户:

用以太坊区块链保证Asp.Net Core的API安全(下)

如果你现在单击“请求数据”按钮,将获得HTTP响应401。如果单击“登录”按钮,Metamask将提示你签名:

用以太坊区块链保证Asp.Net Core的API安全(下)

签名后,处理程序将对令牌端点进行ajax调用。在此阶段,身份验证方法不会检查任何签名,因此端点将始终发出JWT令牌。一旦收到JWT令牌,客户端就能通过ajax调用安全端点。如果现在单击“请求数据”按钮,将收到HTTP响应200和数据负载:

用以太坊区块链保证Asp.Net Core的API安全(下)

从签名中检索以太坊帐户

到目前为止, 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.signweb3.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.EcRecoverNethereum 提供的离线功能。

缺点:

你需要处理 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工程师不可多得的比特币开发学习课程。

汇智网原创翻译,转载请标明出处。这里是 原文


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Probability and Computing

Probability and Computing

Michael Mitzenmacher、Eli Upfal / Cambridge University Press / 2005-01-31 / USD 66.00

Assuming only an elementary background in discrete mathematics, this textbook is an excellent introduction to the probabilistic techniques and paradigms used in the development of probabilistic algori......一起来看看 《Probability and Computing》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具