用于保护 .NET API 的 4 种授权方法

作者:微信公众号:【架构师老卢】
8-17 18:32
13

概述:身份验证和授权是所有 Web 应用程序的重要组成部分。熟悉 .NET 生态系统中的术语和方法可能特别令人生畏。传统上,这些领域是 .NET 中一些较大的痛点,许多开发人员发现与其他技术相比,实现适当的身份验证机制不必要地困难。然而,只了解一些核心原则和方法可以使它更容易接近。在本文中,我们将介绍四种最常见的授权方法,并指导您理解和选择最适合您需求的方法。授权和身份验证的区别在我们探讨处理身份验证和授权的不同方法之前,请务必澄清这两个概念之间的区别。尽管身份验证和授权通常可以互换使用,但它们在访问控制领域中具有不同的用途。身份验证意味着识别您是谁。当我们对系统进行身份验证或登录时,我们实际上是在

身份验证和授权是所有 Web 应用程序的重要组成部分。熟悉 .NET 生态系统中的术语和方法可能特别令人生畏。传统上,这些领域是 .NET 中一些较大的痛点,许多开发人员发现与其他技术相比,实现适当的身份验证机制不必要地困难。

然而,只了解一些核心原则和方法可以使它更容易接近。在本文中,我们将介绍四种最常见的授权方法,并指导您理解和选择最适合您需求的方法。

授权和身份验证的区别

在我们探讨处理身份验证和授权的不同方法之前,请务必澄清这两个概念之间的区别。尽管身份验证和授权通常可以互换使用,但它们在访问控制领域中具有不同的用途。

身份验证意味着识别您是谁。当我们对系统进行身份验证或登录时,我们实际上是在向身份验证实体声明我们就是我们所说的人。这通常是使用用户名/电子邮件和密码完成的。一旦系统验证了此个人或实体是他们声称的身份,身份验证就完成了,并且被授予访问权限。

授权是指您作为经过身份验证的实体被允许执行的操作。系统确认您的身份后,会确定您是否具有访问特定资源的适当权限。

本文将主要重点介绍授权以及促进授权的机制。身份验证通常涉及存储用户数据并使用哈希和加盐等技术验证密码。但是,这本身就是一个主题,在本文中,我们主要关注授权方面。

1. Json Web 令牌

什么是 Json Web 令牌?

要了解 JWT 的用法,我们需要介绍一些基本原则。

JWT 是一种开放标准 (RFC 7519),旨在以 JSON 对象的形式在两方之间安全地传输数据。此数据使用密钥进行数字签名,允许拥有密钥的其他方对其进行验证和信任。这使它成为促进授权的理想技术。一旦客户端通过身份验证,他们将获得一个 JWT。对服务器的每个后续请求都将包含令牌,服务器将验证该令牌。根据有效负载中包含的信息,服务器将允许客户端访问特定路由和资源。

JWT 是一种非常流行的处理授权的方法,是您武器库中的绝佳工具。

JWT 由三个部分组成,由一个点 (.) 分隔。

xxxxx.yyyyy.zzzzz

这些部分被称为标头、有效载荷和签名。

1. 标题

标头包含有关令牌类型和签名算法的信息:

{  
  "alg": "HS256",  
  "typ": "JWT"  
}

2. 有效载荷

有效载荷包含我们希望在两个通信方之间交换的有关某个实体的实际数据或_声明_。有效负载通常包含用户数据:

{  
  "sub": "1234567890",  
  "name": "John Doe",  
  "admin": true,  
  "iat": 1516239022  
}

3. 签名

最后,签名用于验证 JWT 是否未被更改或操纵。它通过使用标头中指定的算法对编码的标头、有效负载和密钥进行签名来实现此目的。如果 JWT 中包含的数据发生更改,它将被服务器的签名验证检测到并被拒绝。

授权流程

JWT 的防篡改特性使其成为在两方之间安全交换信息的理想选择。由于令牌已签名,因此我们可以验证发送者的身份,并检测令牌中包含的数据是否已被更改。

使用 JWT 的典型授权流程如下所示:

  • 登录:客户端向服务器发送带有用户名和密码等凭据的请求。
  • 令牌生成:服务器验证凭据。通常,这涉及根据数据库中的密码哈希检查密码。如果有效,服务器将生成包含用户信息的 JWT,并将其发送回客户端。
  • 令牌存储:客户端将 JWT 存储在 cookie 中。
  • 后续请求:对于任何未来的请求,客户端会在 HTTP 授权标头中发送 JWT。
  • 令牌验证:服务器验证 JWT 的签名,并从令牌负载中提取用户信息以验证请求。

在 .NET API 中设置 JWT 授权

设置 Json Web 令牌身份验证可以相当简单地完成。让我们看一个例子。

  1. 安装必要的软件包
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer  
Install-Package Microsoft.IdentityModel.Tokens  
Install-Package Microsoft.IdentityModel.JsonWebTokens

2. 在 Program.cs 中配置 Json Web Token 身份验证服务。

using Microsoft.AspNetCore.Authentication.JwtBearer;  
using Microsoft.IdentityModel.Tokens;  
using System.Text;  
  
var builder = WebApplication.CreateBuilder(args);  
  
var key = "this is my custom Secret key for authentication"; // This should be stored securely, e.g., in environment variables  
  
builder.Services.AddAuthentication(options =>  
    {  
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;  
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;  
    })  
    .AddJwtBearer(options =>  
    {  
        options.TokenValidationParameters = new TokenValidationParameters  
        {  
            ValidateIssuer = false,  
            ValidateAudience = false,  
            ValidateLifetime = true,  
            ValidateIssuerSigningKey = true,  
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key))  
        };  
    });  
  
builder.Services.AddControllers();  
builder.Services.AddEndpointsApiExplorer();  
builder.Services.AddSwaggerGen();  
  
var app = builder.Build();  
  
if (app.Environment.IsDevelopment())  
{  
    app.UseSwagger();  
    app.UseSwaggerUI();  
}  
  
app.UseAuthentication();  
app.UseAuthorization();  
  
app.MapControllers();  
  
app.Run();

首先,我们定义一个将用于令牌签名的密钥。

**注意:**确保密钥安全存储。出于演示目的,此处以纯文本形式定义它,但在生产应用程序中,应安全地存储和访问它,例如通过环境变量或机密管理服务。

然后,我们设置身份验证服务,并指定我们希望使用 JSON Web 令牌。我们设置为检查令牌是否使用正确的密钥进行签名。这样可以确保令牌未被篡改,并且来自受信任的来源。最后,我们使用之前定义的密钥指定签名密钥。ValidateIssuerSigningKeytrue

3. 为登录端点创建控制器

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;

namespace JsonWebTokens.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthController : ControllerBase
    {
        private readonly string _key;

        public AuthController(IConfiguration config)
        {
            _key = "this is my custom Secret key for authentication";
        }

        [HttpPost("login")]
        public IActionResult Login([FromBody] UserLogin userLogin)
        {
            if (userLogin.Username == "test" && userLogin.Password == "password")
            {
                var token = GenerateJwtToken(userLogin.Username);
                return Ok(new { token });
            }

            return Unauthorized();
        }

        private string GenerateJwtToken(string username)
        {
            var claims = new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, username),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            };

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_key));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var token = new JwtSecurityToken(
                issuer: null,
                audience: null,
                claims: claims,
                expires: DateTime.Now.AddMinutes(30),
                signingCredentials: creds);

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

    public class UserLogin
    {
        public string Username { get; set; }
        public string Password { get; set; }
    }
}

在此示例中,一个简单的 POST 端点接受正文中的一个对象。然后,我们检查提供的凭据是否与预期匹配。在实际生产应用程序中,您通常会从数据库中读取用户,并根据数据库中存储的哈希版本检查提供的密码。如果凭据有效,我们将生成令牌并将其返回给客户端。令牌有效负载存储用户名,其过期时间设置为 30 分钟。UserLogin

4. 通过授权保护端点

现在,我们可以继续向希望保护的端点添加授权:

[Route("api/[controller]")]
[ApiController]
public class WeatherForecastController : ControllerBase
{
    [HttpGet]
    [Authorize]
    public IActionResult Get()
    {
        return Ok(new { message = "This is a protected endpoint" });
    }
}

我们用属性修饰要保护的控制器,以指示控制器使用我们之前定义的身份验证配置。如果未提供令牌,用户将被拒绝,并显示 401 状态代码。[Authorize]

JSON Web 令牌适用于需要对授权机制进行更精细控制的情况。多亏了令牌有效载荷,我们可以提取有关用户的信息,并根据这些信息限制对某些资源的访问,使其成为一种理想且非常流行的授权技术。

2. OAuth 2.0 操作系统

什么是 OAuth 2.0?

OAuth 2.0 是一种开放标准,旨在允许应用程序代表用户安全访问托管在第三方服务上的服务或资源。它被广泛使用,可以被认为是在线授权的事实行业标准。OAuth 2.0 提供了多个流程或“配方”,用于说明应如何处理不同方案的授权。这些流程也称为授权类型,并规定了在各种情况下应如何处理授权。

OAuth 2.0 只是一个授权协议,不处理用户身份验证。它旨在提供对某些资源的访问,并使用访问令牌来执行此操作。最常见的是,JWT 将用于此目的。因此,JWT 和 OAuth 2.0 不是两种截然不同、互斥的技术。相反,JWT 是一种可以在 OAuth 2.0 流程中集成和使用的格式。

角色

OAuth 2.0 中的角色是指 OAuth 系统的不同组件。理解这些很重要。

  • 资源所有者:授权客户端访问他们拥有的资源的用户或系统。
  • 客户端:希望_代表_资源所有者访问特定资源的应用程序。
  • 授权服务器:对资源所有者进行身份验证并向客户端颁发访问令牌的服务器。
  • 资源服务器:托管客户端想要访问的受保护资源(通常是 API)的服务器。

使用客户端凭据授予进行授权

如上所述,OAuth 2.0 提供了不同的授权类型或流程,用于处理各种授权情况。其中一种授权类型是客户凭证授权,我们将在本文中重点介绍。此流程用于不涉及用户的机器对机器 (M2M) 通信。这是一种非常常见的方法,您可能会以一种或另一种方式遇到。在客户端凭据授予方案中,客户端代表自己执行操作,这意味着客户端本身需要访问特定资源。例如,这可能是一个后端应用程序(客户端),需要访问外部 API(资源服务器)以获取数据进行内部处理。

客户凭据授予非常常见,也许是最简单的授予类型。典型的流程如下所示:

  1. 客户端身份验证: 后端应用程序(客户端)具有其客户端 ID 和客户端密钥,用于向授权服务器进行身份验证。
  2. 访问令牌请求: 后端应用程序使用其客户端 ID、客户端密钥和授权类型向授权服务器的令牌端点发送 POST 请求。
  3. 访问令牌响应: 授权服务器验证客户端的凭据,并使用访问令牌进行响应。
  4. 资源请求:后端应用程序使用访问令牌从外部 API(资源服务器)请求数据。
  5. 资源响应:资源服务器验证访问令牌,并使用请求的数据进行响应。

在这个小型 .NET 示例中,我们将创建一个既充当授权服务器又充当资源服务器的应用程序。通常,这两个实体是分开的,但对于更小和集成度更高的应用程序,组合方法可能有效,并且可以降低复杂性和延迟。针对您的特定情况,考虑组合方法和分离方法之间的权衡。

  1. 和以前一样,我们从安装必要的软件包开始。
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer  
Install-Package Microsoft.IdentityModel.Tokens  
Install-Package Microsoft.IdentityModel.JsonWebTokens

2. 我们再次在 Program.cs 中创建必要的 JWT 配置

using Microsoft.AspNetCore.Authentication.JwtBearer;  
using Microsoft.IdentityModel.Tokens;  
using System.Text;  
  
var builder = WebApplication.CreateBuilder(args);  
  
var key = "this is my custom Secret key for authentication"; // This should be stored securely, e.g., in environment variables  
  
builder.Services.AddAuthentication(options =>  
    {  
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;  
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;  
    })  
    .AddJwtBearer(options =>  
    {  
        options.TokenValidationParameters = new TokenValidationParameters  
        {  
            ValidateIssuer = true,  
            ValidateAudience = true,  
            ValidateIssuerSigningKey = true,  
            ValidIssuer = "https://localhost:7232",  
            ValidAudience = "https://localhost:7232",  
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key))  
        };  
    });  
  
builder.Services.AddAuthorization();  
builder.Services.AddControllers();  
builder.Services.AddEndpointsApiExplorer();  
builder.Services.AddSwaggerGen();  
  
var app = builder.Build();  
  
// Configure the HTTP request pipeline.  
if (app.Environment.IsDevelopment())  
{  
    app.UseSwagger();  
    app.UseSwaggerUI();  
}  
  
app.UseRouting();  
app.UseAuthentication();  
app.UseAuthorization();  
app.MapControllers();  
app.Run();

在这种情况下,我们启用 and 选项。这为我们的令牌增加了一层额外的安全保护层,确保我们的应用程序将发行实体和接收实体都编码到令牌中。通过验证发行者和受众,我们使潜在攻击者更难滥用被盗的令牌。在您事先知道将被授权与您的应用程序交互的特定应用程序的情况下,这一点尤为重要。ValidateIssuerValidateAudience

3. 我们创建令牌端点。

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;

namespace OAuth2._0.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class TokenController : ControllerBase
    {
        private const string ClientId = "1";
        private const string ClientSecret = "secret";
        private const string Issuer = "https://localhost:7232";
        private const string Audience = "https://localhost:7232";
        private const string Key = "this is my custom Secret key for authentication";

        [HttpPost("token")]
        public IActionResult Token([FromForm] string client_id, [FromForm] string client_secret)
        {
            if (client_id == ClientId && client_secret == ClientSecret)
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                var key = Encoding.ASCII.GetBytes(Key);
                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Subject = new ClaimsIdentity(new[] { new Claim("sub", client_id) }),
                    Expires = DateTime.UtcNow.AddHours(1),
                    Issuer = Issuer,
                    Audience = Audience,
                    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
                        SecurityAlgorithms.HmacSha256Signature)
                };
                var token = tokenHandler.CreateToken(tokenDescriptor);
                var tokenString = tokenHandler.WriteToken(token);

                return Ok(new
                {
                    access_token = tokenString,
                    token_type = "Bearer",
                    expires_in = 3600, // 1 hour in seconds
                });
            }

            return BadRequest("Invalid client credentials");
        }
    }
}

在生产应用程序中,通常会有一个底层系统存储客户端 ID 和客户端密钥。然后,将这两个值发送给希望获得资源访问权限的受信任系统(客户端)。在这个小示例中,我们只是对这两个值进行硬编码,但要明白在生产应用程序中应该更优雅地处理这个问题。

我们在请求中接受 和,并检查这些值是否与预期值匹配。如果一切正常,我们将继续创建一个令牌并添加必要的声明。请注意,在这种情况下,我们会添加 Issuer 和 Audience。client_idclient_secretmultipart/form-data

响应按以下格式返回给客户端:

{  
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwibmJmIjoxNzIzMDE4MDM5LCJleHAiOjE3MjMwMjE2MzksImlhdCI6MTcyMzAxODAzOSwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NzIzMiIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjcyMzIifQ.0HpnhsU-Ud2zO8owCHeK5xcSIDJzU4OTLmi0LsifMx0",  
    "token_type": "Bearer",  
    "expires_in": 3600  
}

在这里,我们可以看到授权服务器已经向我们(客户端)颁发了一个 Bearer 令牌,该令牌将在一小时后过期。

4. 我们现在可以使用令牌访问受保护的资源。

    [Route("api/[controller]")]
    [ApiController]
    public class ResourceController : ControllerBase
    {
        [HttpGet]
        [Authorize]
        public IActionResult Get()
        {
            return Ok("Protected resource data");
        }
    }

用法

OAuth 2.0 是一个行业标准框架,用于处理各种方案的授权。在上面的简单示例中,我们演示了如何在典型的机器到机器场景中处理授权,但这只是众多用例之一。OAuth 2.0 可用于多种环境,包括 Web 应用程序、移动应用程序和 IoT 设备,无论平台如何,都能提供统一的授权方法。它有据可查并被广泛采用,使开发人员更容易实现。此外,还有许多工具和库可用于不同的编程语言,可以简化 OAuth 2.0 集成过程。

3. 基本认证

什么是基本身份验证?

在前两章中,我们研究了一些更复杂和可扩展的授权机制。对于没有相同可伸缩性和灵活性要求的小型应用程序,基本身份验证提供了一种更简单明了的方法,尽管安全性和可扩展性较差,用于强制实施对 Web 资源的访问控制。

基本身份验证内置于 HTTP 规范中,并允许客户端通过发送以特定格式编码的用户名和密码来向服务器验证自身。

基本身份验证的工作原理

使用基本身份验证的典型流程如下所示。

  1. 客户端响应:然后,客户端发送一个带有 Authorization 标头的请求,其中包含编码为 base64 的凭据(用户名和密码)。
  2. 服务器验证:服务器解码 base64 字符串以检索用户名和密码,然后验证这些凭据。
  3. 授予/拒绝访问权限:如果凭据有效,则服务器将授予对所请求资源的访问权限。否则,服务器将以 401 未经授权进行响应。

这个想法是,对于向资源服务器的每个请求,用户名和密码都以这种方式发送。然后,服务器将解码并验证这些凭据,以确定是否应允许客户端访问。

这是一个非常简单的解决方案,但应谨慎使用,因为它会带来一些严重的安全风险。Base64 只是简单的编码,可以很容易地反转,这意味着我们基本上是以纯文本形式发送凭据。

如果您决定采用此方法,请始终确保使用 HTTPS 通过网络加密凭据。

1. 我们从创建一个身份验证处理程序开始

using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Text.Encodings.Web;

public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    public BasicAuthenticationHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock)
        : base(options, logger, encoder, clock)
    {
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        if (!Request.Headers.ContainsKey("Authorization"))
        {
            return AuthenticateResult.Fail("Missing Authorization Header");
        }

        try
        {
            var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
            var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
            var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':');
            var username = credentials[0];
            var password = credentials[1];

            // Validate the username and password (this is just an example, you should validate against a user store)
            if (username == "admin" && password == "password")
            {
                var claims = new[] {
                    new Claim(ClaimTypes.Name, username)
                };
                var identity = new ClaimsIdentity(claims, Scheme.Name);
                var principal = new ClaimsPrincipal(identity);
                var ticket = new AuthenticationTicket(principal, Scheme.Name);

                return AuthenticateResult.Success(ticket);
            }
            else
            {
                return AuthenticateResult.Fail("Invalid Username or Password");
            }
        }
        catch
        {
            return AuthenticateResult.Fail("Invalid Authorization Header");
        }
    }
}

这是相当直截了当的。此处理程序将尝试提取 Authorization 标头。如果未设置授权标头,我们将拒绝访问。然后,我们尝试从标头中获取 base64 字符串,并提取用户名和密码。同样,出于说明目的,我们只需根据硬编码值验证凭据。通常,您会检查某些用户存储。

如果一切正常,我们将创建一个简单的声明并成功进行身份验证。

2. 在 Program.cs 中注册处理程序

using BasicAuth.Handlers;  
using Microsoft.AspNetCore.Authentication;  
  
var builder = WebApplication.CreateBuilder(args);  
  
builder.Services.AddControllers();  
builder.Services.AddAuthentication("BasicAuthentication")  
    .AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);  
  
builder.Services.AddEndpointsApiExplorer();  
builder.Services.AddSwaggerGen();  
  
var app = builder.Build();  
  
// Configure the HTTP request pipeline.  
if (app.Environment.IsDevelopment())  
{  
    app.UseSwagger();  
    app.UseSwaggerUI();  
}  
  
app.UseHttpsRedirection();  
app.UseRouting();  
  
app.UseAuthentication();  
app.UseAuthorization();  
  
app.MapControllers();  
  
  
app.Run();

在这里,我们将应用程序配置为使用基本身份验证方案,并指定我们的处理程序。

3. 我们现在可以保护我们的端点

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    [HttpGet]
    [Authorize]
    public IActionResult Get()
    {
        return Ok("Protected resource data");
    }
}

用法

如上所述,基本身份验证提供了一种非常简单的方法,用于对应用程序的请求进行身份验证。但是,由于这种方法具有明显的缺点,因此绝不应用于促进生产环境中的实际访问控制。

过去,我使用基本身份验证在安全性和可伸缩性不是优先事项的简单场景中提供对测试环境的访问。基本身份验证既快速又简单,但应谨慎使用,并且仅在适当的情况下使用。考虑其他方法是否更适合您的特定情况。

4. API Key授权

什么是 API 密钥授权?

API 密钥授权是一种简单且广泛使用的方法,用于控制对 API 的访问。此方法非常适合需要授予对一个或多个终结点的访问权限,但不需要对用户的实际身份进行精细控制的情况。当您需要访问受限制的 API 时,通常在注册它之后,通常采用这种方法。注册后,通常会向客户端颁发 API 密钥,然后可以存储该密钥并用于访问特定资源。发行实体保留根据需要撤销 API 密钥并使之失效的能力。

从本质上讲,API 密钥是分配给客户端的唯一标识符。客户端在其 API 请求中包含此密钥,以获取对服务的访问权限。API 密钥通常在标头中发送到服务器,在那里使用自定义逻辑进行提取和验证。x-api-key

API 密钥的美妙之处在于它们为开发人员提供了实现授权逻辑的灵活性。通常,客户信息与其各自的 API 密钥和任何其他相关信息一起存储在数据库中。例如,您可以存储客户端的 IP 地址,并验证请求是否来自预期的客户端。或者,您可能会在一段时间后使 API 密钥失效。

关键是,围绕授权的所有业务逻辑都可以由开发人员自由配置,并且可以根据需要进行复杂或简单的配置。这使得 API 密钥授权成为许多应用程序的不错选择,在这些应用程序中,与更复杂的授权方法相比,这些应用程序需要简单性和灵活性。

API 密钥授权的工作原理

典型的 API 密钥授权流程如下所示:

  • API 密钥生成:服务器为每个客户端生成一个唯一的密钥。此键通常是一长串字母数字字符。
  • 客户端使用情况:客户端在请求头中包含此密钥。
  • 服务器验证:服务器接收请求,并根据存储的有效密钥列表检查密钥。
  • 授权:如果密钥有效,服务器将处理该请求。否则,访问将被拒绝。

如上所述,服务器验证步骤可以按照开发人员的选择来实现。

客户端发送的 API 密钥通常会在一段中间件中提取,中间件会拦截 API 调用并执行授权逻辑。

  1. 我们从创建授权中间件开始。
public class ApiKeyMiddleware
{
    private readonly RequestDelegate _next;
    private const string API_KEY_HEADER_NAME = "X-Api-Key";

    public ApiKeyMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        if (!context.Request.Headers.TryGetValue(API_KEY_HEADER_NAME, out var extractedApiKey))
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("API Key is missing.");
            return;
        }

        var apiKey = "SuperSecret"; // Will normally come from a store/configuration/database

        if (!apiKey.Equals(extractedApiKey))
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("Unauthorized client.");
            return;
        }

        await _next(context);
    }
}

在这里,我们首先注入 RequestDelegate,如果一切顺利,我们用它来在 HTTP 请求管道中继续发送请求,或者在授权失败时终止请求。

然后,我们尝试从适当的标头中提取 API 密钥,并将其与预期的 API 密钥进行检查。同样,API 密钥通常应该在某种形式的存储中进行处理,但对于这个简单的说明,我们只是对其进行了硬编码。

如果键不匹配,我们返回 401。如果一切正常,我们调用 _next 将请求发送到请求管道中的下一个中间件。

2. 在 Program.cs 中注册中间件

using APIKey.Middleware;  
  
var builder = WebApplication.CreateBuilder(args);  
  
builder.Services.AddControllers();  
builder.Services.AddEndpointsApiExplorer();  
builder.Services.AddSwaggerGen();  
  
  
var app = builder.Build();  
  
app.UseMiddleware<ApiKeyMiddleware>();  
  
// Configure the HTTP request pipeline.  
if (app.Environment.IsDevelopment())  
{  
    app.UseSwagger();  
    app.UseSwaggerUI();  
}  
  
app.UseHttpsRedirection();  
app.UseRouting();  
app.MapControllers();  
  
app.Run();

就这样。对于 API 收到的每个请求,中间件将提取 API 密钥并确保其有效,然后允许请求继续沿着请求管道向下移动。

用法

如上所述,在我们需要向特定客户端颁发密钥而不需要非常精细的身份管理的情况下,API 密钥授权是一种很好的方法。这适用于机器对机器通信,并且可以扩展以适应特定用例。在上面的示例中,我们研究了最简单的解决方案,但授权机制可以根据您的需求进行配置。与基本身份验证不同,我们不需要将凭据存储在实际令牌中,这使其成为一种更安全的处理授权的方法。

在本文中,我们研究了在 .NET API 中处理授权的四种常用方法,每种方法都有其自身的优点和缺点。我们讨论了 JWT(JSON Web 令牌)、OAuth、基本身份验证和 API 密钥授权。了解这些方法至关重要,可以帮助您选择适合特定应用需求的方法。

阅读排行