所有文章 > 日积月累 > 用于保护 .NET API 的 4 种授权方法
用于保护 .NET API 的 4 种授权方法

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

身份验证和授权是所有 Web 应用程序的重要组成部分。.NET 生态系统中的术语和方法往往让人感到特别难以掌握。长久以来,这些领域一直是 .NET 中的一些重大痛点,许多开发人员觉得,与其他技术相比,在 .NET 中实现适当的身份验证机制变得异常艰难,有些得不偿失。

但是,只要掌握一些核心原则和方法,就能让我们更容易接近并解决这个问题。在本文中,我们将介绍四种最常见的授权方法,并帮助您了解如何选择最适合您需求的方法。

授权和身份验证之间的区别

在深入探讨处理身份验证与授权的各种方法之前,我们首先需要明确这两个概念之间的区别。尽管它们经常互换使用,但身份验证和授权在访问控制领域中的作用是不同的。

身份验证是指确认您的身份的过程。当我们尝试登录系统或进行身份验证时,实际上是在向身份验证系统声明自己的身份。这通常使用用户名/电子邮件和密码完成。一旦系统验证了此个人或实体是他们声称的身份,身份验证即完成,并授予访问权限。

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

本文将主要关注授权和促进授权的机制。身份验证通常包括存储用户数据,并使用哈希、加盐等技术来验证用户密码。不过,这本身就是一个复杂的话题,本文中我们将主要聚焦于授权方面的内容。

1. Json Web 令牌

什么是 Json Web 令牌?

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

JWT 是一种开放标准 (RFC 7519),旨在以 JSON 对象的形式在两方之间安全地传输数据。使用密钥对数据进行数字签名,使得持有相应验证密钥的其他方能够确认其真实性和可信度。这一特性使其成为推动授权流程高效进行的优选技术。当客户端成功完成身份验证后,系统会向其颁发JWT。对服务器的每个后续请求都将包含令牌,服务器将验证该令牌。然后,根据有效负载中包含的信息,服务器将允许客户端访问特定的路由和资源。

JWT(JSON Web Tokens)是一种极为流行的授权处理方式,同时也是您工具箱中的得力助手。

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

这些部分称为 header、payload 和 signature。

1. 标题

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

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

2. 有效载荷

JWT的有效负载部分包含了我们希望在两个通信方之间传递的关于某个实体的实际数据或声明信息。有效负载通常包含用户数据:

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

3. 签名

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

授权流程

JWT的防篡改特性使其成为双方之间安全交换信息的优选方案。由于令牌已经过签名,我们可以确认发件人的身份,并检查令牌中的数据是否已被更改。

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

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

在 .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();

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

注意:为了确保密钥的安全,应当将其妥善存储。虽然在此处为了演示方便以纯文本形式展示,但在实际生产应用程序中,必须确保密钥的安全存储与访问。例如通过环境变量或 Secret Management 服务。

然后,我们配置身份验证服务,并指明希望使用JSON Web Token(JWT)。我们设置为验证令牌是否使用了正确的密钥进行签名(validate_issuer_signing_key: true)。这一步骤至关重要,因为它能确保令牌未被篡改,且确实来自一个受信任的来源。最后,我们使用之前定义的密钥(secret key)来指定签名的密钥。

3.为Login Endpoint创建一个控制器,使用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 终端节点接受正文中的对象。然后,我们检查提供的凭证是否与预期匹配。在实际的 UserLogin生产应用程序中,您通常会从数据库中读取用户,并根据数据库中存储的哈希版本检查提供的密码。若凭证验证通过,我们将生成一个令牌,并将其返回给客户端。该令牌的有效负载中会包含用户名信息,并且设置其过期时间为30分钟。

4. 使用授权保护端点

现在,我们可以继续为要保护的端点添加授权:

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

我们使用属性(attribute)来修饰那些我们希望进行保护的controller,以此指示这些controller应使用我们之前定义的认证配置。如果请求中未提供有效的令牌,用户将被拒绝访问,并收到401状态码(未授权)。具体做法是在controller类上添加[Authorize]属性。

JSON Web Token(JWT)特别适用于需要精细控制授权机制的场景。通过令牌的有效负载,我们可以提取与用户相关的信息,并据此限制对某些资源的访问,因此,它成为了一种理想且极为流行的授权技术。

2. OAuth 2.0

什么是 OAuth 2.0?

OAuth 2.0 是一种开放标准,旨在允许应用程序代表用户安全访问托管在第三方服务上的服务或资源。它得到了广泛的应用,几乎可以被视为在线授权的行业标准。OAuth 2.0 定义了多种流程或“方案”,用于指导在不同场景下如何处理授权问题。这些流也称为授权类型,并指示应如何针对各种情况处理授权。

OAuth 2.0 只是一个授权协议,不处理用户身份验证。它旨在提供对某些资源的访问,并使用访问令牌来实现此目的。在此类场景中,JWT(JSON Web Token)是最常被采用的方式。因此,JWT 和 OAuth 2.0 并不是两种截然不同的、互斥的技术。相反,JWT 是一种可以在 OAuth 2.0 流程中集成和使用的格式。

角色

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

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

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

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

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

  1. 客户端身份验证: 后端应用程序(客户端)具有其客户端 ID 和客户端密钥,用于向授权服务器进行身份验证。
  2. Access Token 请求: 后端应用程序将 POST 请求发送到授权服务器的令牌终端节点,其中包含其客户端 ID、客户端密钥和授权类型。
  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();

在这个方案中,我们启用了ValidateIssuerValidateAudience选项。这通过确保我们的应用程序将颁发者和接收者(受众)的信息编码到令牌中,为我们的令牌增加了额外的安全层级。通过验证发行者和受众,我们能够使得潜在攻击者更难滥用被盗的令牌。在您事先了解将与您的应用程序进行交互的特定应用程序的情况下,这一步骤尤为重要。

3. 创建 Token 终端节点。

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");
}
}
}

在生产应用程序中,通常会有一个底层系统存储 Client ID 和 Client Secret。随后,这两个值(指发行者和受众的标识符)将被授予那些希望访问资源的可信系统(即客户端)。在这个简单的示例中,我们直接对这两个值进行了硬编码,但请注意,在实际的生产应用程序中,应该采用更为妥善的方式来处理这些值。

我们在请求中接收client_idclient_secret,并校验这两个值是否与我们的预期相匹配。如果校验通过,我们将继续生成JWT令牌,并向其中添加必要的声明信息。特别地,在这个场景下,我们会添加Issuer(发行者)和Audience(受众)声明。这里提到的multipart/form-data通常指的是请求的内容类型,它允许我们以表单格式提交数据,包括client_idclient_secret等信息。

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

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

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

4. 现在可以使用 Token Access 受保护的资源。

 [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因其有据可查且被广泛应用,为开发人员提供了更便捷的实现途径。此外,还有众多针对不同编程语言的工具和库可供使用,这些工具和库能够简化OAuth 2.0的集成流程。

3. 基本身份验证

什么是基本身份验证?

在前两章中,我们了解了一些更复杂且可扩展的授权机制。对于那些没有相同级别伸缩性和灵活性需求的小型应用程序而言,基本身份验证提供了一种更为简单直接的方法来实施对Web资源的访问控制,尽管它在安全性和可扩展性方面相对较弱。

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

基本身份验证的工作原理

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

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

其基本思路是:用户每次向资源服务器发送请求时,都会以特定方式携带用户名和密码。服务器接收到请求后,会先对这些凭据进行解码,然后验证其有效性,最终决定是否允许客户端访问相应的资源。

这是一个非常简单的解决方案,但应谨慎使用,因为它存在一些严重的安全风险。Base64编码仅仅是一种简单的编码方式,它可以很容易地被解码,这意味着通过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 标头。如果未设置 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 Key 授权?

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

从本质上来说,API密钥是一个分配给客户端的唯一标识符。客户端会在其API请求中包含这个密钥,以此获取对服务的访问权限。通常,API密钥会以x-api-key的形式作为请求头发送到服务器,服务器则会通过自定义的逻辑来提取并验证这个密钥。

API密钥的优势在于为开发人员提供了在实施授权逻辑时的极大灵活性。一般情况下,客户端的信息会与它们各自的API密钥以及其他相关的信息一同存储在数据库中。例如,您可以存储客户端的 IP 地址并验证请求是否来自预期的客户端。或者,您也可以在一段时间后使 API 密钥失效。

关键在于,开发人员可以自由地配置所有与授权相关的业务逻辑,这些逻辑可以根据实际需求既复杂又精细,也可以相对简单直接。这使得 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密钥授权无疑是一个很好的选择。这适用于机器对机器通信,并且可以扩展以适应特定的用例。在上面的示例中,我们研究了最简单的解决方案,但授权机制可以根据您的需要进行配置。与基本身份验证相比较,API密钥授权的一个显著优势在于我们无需将敏感凭证直接存储在实际使用的令牌(即API密钥)中,这一特性使得它成为了一种更为安全的授权处理方式。

总结

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

授权机制可能会让人感到难以捉摸,但本文旨在为您提供一个清晰易懂、易于入门的起点。

原文链接:https://medium.com/@aschultzme/4-authorization-methods-for-securing-your-net-api-0075f2cf755b?source=search_post

#你可能也喜欢这些API文章!