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

BitLuminaryMaster
• 阅读 1917

去中心化应用程序(DApp)的常见设计不仅依赖于以太坊区块链,还依赖于API层。在这种情况下,DApp通过用户的以太坊帐户与智能合约进行交互,并通过交换用户凭据而发布的JWT token与API层进行交互。

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

目标是使用以太坊帐户作为用户凭据来请求JWT Token。

最简单的方法可能是请求用户使用其他随机生成的数据在以太坊上进行交易,然后在发出JWT之前检查交易和随机数据。这种方法有几个副作用:

  • 1.用户必须进行交易并支付gas以进行简单的身份验证。
  • 2.用户必须等待12-120秒(基于耗费的gas)才能完成身份验证过程。
  • 3.每个用户的所有登录操作在以太坊区块链上变得不可公开。

这种方式不实用,并且有一些用户体验限制,我们需要一种方法让用户证明他拥有与他想要用来登录的帐户相关的私钥,而不是只(当然)要求私钥,而不管他是否进行交易。

解决方案

Metamask团队成员Dan Finlay这篇文章向我启发了本教程。基本上,你的DApp可以提示用户使用他的私钥对短信进行签名。此签名操作不会生成交易,并且它由Metamask附加组件透明地处理(顺便说一句,你的帐户需要解锁)。签名后,帐户,消息和签名将发送到API Token endpoint。验证方法首先通过接受签名和明文消息作为输入的函数从签名中推断帐户(也称为公钥)。如果计算的以太坊地址等于用户提供的帐户,则为该帐户发出JWT Token。

请务必注意,整个身份验证流程不需要用户名/密码或OAuth外部服务。用于验证用户身份的机制与以太坊用于保证以太坊区块链安全性的机制相同。这要归功于Go ethereum(Geth)通过Metamask插件提供JSON RPC中的web3.personal.sign

服务器端调用对应的JSON RPC以从签名中检索帐户:web3.personal.ecrecover。在本教程中,我们将构建一个Asp.Net Core 2项目作为API层,并构建一个简单的HTML/javascript客户端作为DApp,以实际演示此身份验证过程。

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

  • 1.从DApp用户单击登录按钮。这需要Metamask提供的web3对象。
  • 2.Metamask要求用户通过JSON RPC的web3.personal.sign签署消息。
  • 3.签名将发送到API层,该层通过JSON RPC的web3.personal.ecrecover验证帐户。
  • 4.验证后,API层将发布JWT。

先决条件

  • 1.为Chrome或Firefox安装Metamask插件。这个附加组件“将以太坊带到你的浏览器上”。实际上,Metamask提供了一个web3对象,用于与你的DApp中的以太坊区块链进行交互,处理你的私钥并在浏览器中管理交易。
  • 2.可选的。运行Geth节点。我将向你展示两种从签名中恢复以太坊帐户的方法,其中一种方法需要你的API层针对Geth节点调用JSON RPC。注意:Infura现在还不行,因为它们不允许大多数web3.personal.*的JSON RPC接口。出于开发目的,运行Geth节点非常简单。在生产环境中,出于安全考虑,运行Geth节点并不是一项简单的任务。最好的方法是依靠AWS或Azure提供的区块链即服务堆栈(BaaS)。
  • 3.开发堆栈:Visual Studio 2017和节点包管理器(NPM)。
  • 4.以太坊/Asp.Net核心/前端开发的基础知识,JWT认证流程的基础知识。

开始

打开Visual Studio 2017,创建EthereumJwtSolution并添加两个Asp.Net Core 2 Web应用程序项目:EthereumJwtApiEthereumJwtClient。为两个项目选择空项目脚手架。

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

EthereumJwtClient只是一个HTML/Javascript客户端。我们将在Asp.Net Core上构建客户端应用程序,只是为了在IIS Express上轻松运行它。

我们需要准备EthereumJwtApi来创建和处理JWT token,以保护一些安全端点。任务很简单,因为Asp.Net Core 2有一个内置的JWT机制,可以插入我们的应用程序。 打开Startup.cs并修改ConfigureServices方法:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Issuer"],
            ValidAudience = Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
        };
    });

    services.AddCors(options =>
    {
        options.AddPolicy("CorsPolicy",
            builder => builder.AllowAnyOrigin()
              .AllowAnyMethod()
              .AllowAnyHeader()
              .AllowCredentials()
        .Build());
    });

    services.AddMvc();

然后修改Configure方法:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.UseCors("CorsPolicy");

app.UseAuthentication();

app.UseMvc();

我们告诉我们的API应用程序使用JWT身份验证服务。为了与我们的用户合作,我们还需要配置Cors策略。我们在appsetting.json中定义设置JWT配置:

"Jwt": {
    "Key": "averysecretpassphrase", // A random and secure passhphrase
    "Issuer": "http://localhost:49443/", // This API base URI
    "Audience": "http://localhost:51149/" // The client base URI
  },

为测试目的创建一个简单的可能安全端点:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    // GET api/values
    [HttpGet, Authorize]
    public IEnumerable<string> Get()
    {
        return new string[] { "Secret 1", "Secret 2" };
    }
}

TokenController.cs将处理JWT请求和相关的token问题:

[Route("api/[controller]")]
public class TokenController : Controller
{
    private IConfiguration _config;

    public TokenController(IConfiguration config)
    {
        _config = config;
    }

    [AllowAnonymous]
    [HttpPost]
    public async Task<IActionResult> CreateToken([FromBody]LoginVM login)
    {
        var user = await Authenticate(login);

        if (user != null)
        {
            var tokenString = BuildToken(user);
            return Ok(new { token = tokenString });
        }

        return Unauthorized();
    }

    private string BuildToken(UserVM user)
    {
        var claims = new[] {
            new Claim(JwtRegisteredClaimNames.Sub, user.Account),
            new Claim(JwtRegisteredClaimNames.GivenName, user.Name),
            new Claim(JwtRegisteredClaimNames.Email, user.Email),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(_config["Jwt:Issuer"],
          _config["Jwt:Audience"],
          claims,
          expires: DateTime.Now.AddMinutes(30),
          signingCredentials: creds);

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    private async Task<UserVM> Authenticate(LoginVM login)
    {
        // TODO: this method will authenticate the user recovering the Ethereum address from signature using the Geth RPC web3.personal.ecrecover API

        UserVM user = user = new UserVM { Account = login.Account, Name = string.Empty, Email = string.Empty };

        return user;
    }

    private async Task<UserVM> Authenticate2(LoginVM login)
    {
        // TODO: This method will authenticate the user recovering his Ethereum address through underlaying offline ecrecover method.
        
        UserVM user = user = new UserVM { Account = login.Account, Name = string.Empty, Email = string.Empty };

        return user;
    }

这是一个典型的JWT控制器,核心方法,AuthenticateAuthenticate2尚未实现。一旦实现,他们将完成相同的工作:从签名中恢复以太坊地址,并检查它是否等于客户端提供的以太坊地址。

LoginVM表示客户端提供的用户凭据,UserVM表示“服务器端”登录用户:

public class LoginVM
{
    public string Signer { get; set; } // Ethereum account that claim the signature
    public string Signature { get; set; } // The signature
    public string Message { get; set; } // The plain message
    public string Hash { get; set; } // The prefixed and sha3 hashed message 
}

public class UserVM
{
    public string Account { get; set; } // Unique account name (the Ethereum account)
    public string Name { get; set; } // The user name
    public string Email { get; set; } // The user Email
}

Authenticate方法将SignatureMessage属性作为ecRecover函数的输入,Authenticate2方法将采用SignatureHash属性。我稍后会解释其中的差异。

======================================================================

分享一些以太坊、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

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
可莉 可莉
3年前
10个开源的Python区块链项目
Python不是主流的区块链底层平台开发语言,但是在DApp开发、区块链仿真与数据分析、智能合约安全分析等领域,Python依然是不错的选择。本文介绍了10个最流行的Python区块链项并提供了相应的源代码下载地址。<!more区块链开发教程链接:以太坊(https://www.oschina.net/action/GoToLink
Stella981 Stella981
3年前
Defi数据引擎The Graph调用方法【含源码】
当你尝试访问以太坊智能合约以及DApp产生的区块链数据时,可能会发现很难将数据转换为一种可读的格式。TheGraph提供了一种用于查询以太坊和IPFS网络数据的索引协议,任何人都可以基于其提供的开放API创建并发布索引数据,即subgraph,这使得区块链数据更容易访问。在这个教程中,我们将学习如何使用TheGraph来查询Aave协议数据,使用的技术
Stella981 Stella981
3年前
DeFi研究资源大全,超过100个!
本文为DeFiPulse提供的去中心化金融DeFi协议及应用大全的中文翻译版,内容涵盖借贷产品、DeFi交易、去中心化支付、DeFi钱包、用户接口、DeFi基础设施、数据分析等11个分类,是区块链开发人员及金融从业者研究去中心化金融的必备资源。用自己熟悉的语言学习以太坊DApp开发:Java(https://www.oschina.
Stella981 Stella981
3年前
EthSnarks以太坊混币器【零知识证明】
Miximus是一个用于以太坊区块链的去中心化混币器和匿名转账应用,由EthSnarks作者开发,用于展示零知识证明在以太坊上的实用性。本文介绍Miximus以太坊混币应用的安装使用方法、工作原理和实现细节。用自己熟悉的语言学习以太坊DApp开发:Java(https://www.oschina.net/action/GoToLink?urlh
Wesley13 Wesley13
3年前
NEO从源码分析看UTXO交易
_0x00前言_社区大佬:“交易是操作区块链的唯一方式。”_0x01交易类型_在NEO中,几乎除了共识之外的所有的对区块链的操作都是一种“交易”,甚至在“交易”面前,合约都只是一个小弟。交易类型的定义在Core中的TransactionType中:源码位置:neo/Core/TransactionType
Stella981 Stella981
3年前
Solidity内联汇编简明指南
在用Solidity开发以太坊智能合约时,使用汇编可以直接与EVM交互,降低gas开销成本,更精细的控制智能合约的行为,因此值得Solidity开发者学习并加以利用。本文是Solidity汇编开发的简明教程,旨在帮助你快速熟悉如何在Solidity智能合约代码中嵌入汇编代码。以太坊教程链接:Dapp入门(https://www.oschina.n
Stella981 Stella981
3年前
Chainlink区块链Oracle网络使用指南
Chainlink是一个去中心化的预言机网络,它可以让区块链中的智能合约安全地访问外部世界的数据。在这个教程中,我们将探索chainlink预言机网络的搭建,并学习如何使用预置或自定义的适配器实现智能合约与外部世界数据的桥接。以太坊教程链接:Dapp入门(https://www.oschina.net/action/GoToLink?urlh
冯紫英 冯紫英
1年前
使用 Wagmi 2.0 和 Viem 的教程
Wagmi2.0是一个全新的版本,采用了Viem作为底层库,使得以太坊应用程序开发变得更加高效和便捷。Viem是一个用于与以太坊区块链交互的库,提供了更快、更可靠的操作,并且更好地支持TypeScript。下面的教程将带你一步步了解如何在你的项目中使用Wa