资讯专栏INFORMATION COLUMN

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

xuhong / 2829人阅读

摘要:上一篇用以太坊区块链保证的安全上我们介绍了基本的解决方案,这一篇我们重点来看客户端。是以太坊的实现。以太坊,主要是针对工程师使用进行区块链以太坊开发的详解。这里是原文以太坊区块链的安全设计

上一篇用以太坊区块链保证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层加载一些安全数据:




    
    Ethereum Jwt Client


    

Ethereum Jwt Client

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:
" + 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 += "
  • " + json[i] + "
  • "; } }).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,你将在页面上看到你的帐户:

    如果你现在单击“请求数据”按钮,将获得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 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(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 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工程师不可多得的比特币开发学习课程。

    这里是原文以太坊区块链Asp.Net Core的安全API设计

    文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

    转载请注明本文地址:https://www.ucloud.cn/yun/24351.html

    相关文章

    • 以太区块保证Asp.Net CoreAPI安全(上)

      摘要:去中心化应用程序的常见设计不仅依赖于以太坊区块链,还依赖于层。每个用户的所有登录操作在以太坊区块链上变得不可公开。用于验证用户身份的机制与以太坊用于保证以太坊区块链安全性的机制相同。以太坊,主要是针对工程师使用进行区块链以太坊开发的详解。 去中心化应用程序(DApp)的常见设计不仅依赖于以太坊区块链,还依赖于API层。在这种情况下,DApp通过用户的以太坊帐户与智能合约进行交互,并通过...

      Raaabbit 评论0 收藏0
    • 以太、EOS和Hyperledger等不同区块比较

      摘要:以太坊背后的主要人物是。以太坊通过在区块链上引入智能合约,彻底改变了加密世界。以太坊使用名为以太坊虚拟机的虚拟机执行其智能合约。以太坊最终将利用协议转向权益证明。截至目前,以太坊在可扩展性方面都失败了。 不同的区块链智能合约和区块链技术现在风靡一时。越来越多的人出于某种原因试图进入这个神奇的世界。如果你是这项技术的新手并正在寻找基于区块链的开发平台的快速入门,那么本指南非常适合你。我们...

      harriszh 评论0 收藏0
    • c#工程师Visual Studio开发dapp应程序

      摘要:最初,我对以太坊区块链,智能合约和应用程序提出了大量疑问。即使需要额外的工具来设置开发环境,现在微软将使用新的针对以太坊区块链产品。 最近区块链技术引起了我的注意。我刚开始了解它,就看到了区块链去中心化架构的巨大潜力,并且它能够简化各种现有繁琐的流程,通过各种形式的合约。 作为一名.NET开发人员,我主要使用的是一些提供中心数据源的架构,因此区块链的去中心化概念对我来说是全新的。我很快...

      骞讳护 评论0 收藏0

    发表评论

    0条评论

    最新活动
    阅读需要支付1元查看
    <