深入解析.NET API四大授权方案:JWT、OAuth 2.0、基础认证与API密钥

作者:微信公众号:【架构师老卢】
9-23 14:24
830

认证与授权是所有Web应用程序不可或缺的重要组成部分。在.NET生态系统中,理清相关术语和方法可能尤其令人望而生畏。传统上,这些领域一直是.NET中较大的痛点之一,许多开发者发现,与其他技术相比,实现恰当的认证授权机制异常困难。

然而,只需理解几个核心原则和方法,就能大大降低入门难度。在本文中,我们将探讨四种最常见的授权方案,并指导您理解和选择最适合您需求的方法。

授权与认证的区别 在探讨处理认证和授权的不同方法之前,澄清这两个概念之间的区别非常重要。尽管它们经常被互换使用,但认证和授权在访问控制领域有着不同的目的。

认证旨在确认您的身份。当我们向系统进行认证或登录时,本质上是在向认证方声明我们是所声称的用户。这通常通过用户名/邮箱和密码来完成。一旦系统验证了该个体或实体确实是其所声称的身份,认证即告完成,并授予访问权限。

授权则是指您作为已认证的实体,被允许执行哪些操作。在系统确认您的身份后,它会进一步判断您是否拥有访问特定资源的适当权限。

本文将主要关注授权及其实现机制。认证通常涉及存储用户数据以及使用哈希加盐等技术验证密码。但这本身是一个独立的话题,本文将主要讨论授权方面。

1. JSON Web Tokens 什么是JSON Web Token? 要理解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的典型授权流程如下:

  1. 登录:客户端向服务器发送包含凭据(如用户名和密码)的请求。
  2. 令牌生成:服务器验证凭据。这通常涉及检查密码与数据库中的密码哈希值是否匹配。如果有效,服务器会生成一个包含用户信息的JWT并将其发送回客户端。
  3. 令牌存储:客户端存储JWT,通常存储在Cookie中。
  4. 后续请求:对于未来的任何请求,客户端在HTTP Authorization头中发送JWT。
  5. 令牌验证:服务器验证JWT的签名,并从令牌载荷中提取用户信息以认证请求。

在.NET API中设置JWT授权 设置JSON Web Token认证可以相当简单。让我们看一个例子。

  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"; // 应安全存储,例如在环境变量中
    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 Tokens。我们将ValidateIssuerSigningKey设置为true以检查令牌是否使用正确的密钥签名。这确保了令牌未被篡改且来自可信来源。最后,我们使用之前定义的密钥指定签名密钥。

  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端点接收请求体中的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" });
        }
    }
    

    我们使用[Authorize]特性装饰希望保护的控制器,以指示控制器使用我们之前定义的认证配置。如果未提供令牌,用户将被拒绝并返回401状态码。

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

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提供了不同的授权类型或流程来处理各种授权情况。其中一种授权类型是客户端凭据授权,我们将在本文中重点介绍。此流程用于机器对机器通信,不涉及用户。这是一种极其常见的方法,您很可能会以某种方式遇到。在客户端凭据授权场景中,客户端代表自己行事,意味着客户端本身需要访问某个资源。例如,这可能是一个需要访问外部API以获取数据进行内部处理的后端应用程序。

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

  1. 客户端认证:后端应用程序拥有其客户端ID和客户端密钥,用于向授权服务器进行认证。
  2. 访问令牌请求:后端应用程序向授权服务器的令牌端点发送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"; // 应安全存储,例如在环境变量中
    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. 我们创建令牌端点。

    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小时,以秒为单位
                    });
                }
                return BadRequest("Invalid client credentials");
            }
        }
    }
    

    在生产应用程序中,通常有一个底层系统来存储客户端ID和客户端密钥。这两个值随后被颁发给希望获得资源访问权限的可信系统。在这个小例子中,我们简单地将这两个值硬编码,但请理解在生产应用程序中应以更优雅的方式处理。

    我们接受multipart/form-data请求中的client_idclient_secret,并检查这些值是否符合预期。如果一切正常,我们继续创建令牌并添加必要的声明。注意,在这种情况下我们添加了IssuerAudience

    向客户端返回的响应格式如下:

    {
        "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应用程序、移动应用程序和物联网设备,提供了跨平台的统一授权方法。它文档完善且被广泛采用,使开发者更容易实现。此外,还有适用于不同编程语言的许多工具和库,简化了OAuth 2.0的集成过程。

3. 基础认证 什么是基础认证? 在前两章中,我们看了一些更复杂、可扩展性更强的授权机制。对于没有相同可扩展性和灵活性要求的较小应用程序,基础认证提供了一种更简单直接(尽管安全性较差且可扩展性不高)的方法来强制执行对Web资源的访问控制。

基础认证内置于HTTP规范中,它允许客户端通过发送以特定格式编码的用户名和密码向服务器进行认证。

基础认证如何工作 使用基础认证的典型流程如下:

  1. 客户端请求:客户端向需要认证的资源发送请求。
  2. 服务器质询:如果请求缺少认证信息,服务器响应401 Unauthorized状态码和WWW-Authenticate头,指示使用Basic认证方案。
  3. 客户端响应:客户端随后发送一个包含Authorization头的请求,该头包含用base64编码的凭据(用户名和密码)。
  4. 服务器验证:服务器解码base64字符串以检索用户名和密码,然后验证这些凭据。
  5. 访问授予/拒绝:如果凭据有效,服务器授予对所请求资源的访问权限。如果无效,服务器响应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];
                // 验证用户名和密码(这只是一个示例,您应该针对用户存储进行验证)
                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密钥授权 什么是API密钥授权? API密钥授权是一种简单且广泛使用的控制API访问的方法。这种方法适用于需要向一个或多个端点颁发访问权限,但不需要对用户实际身份进行细粒度控制的情况。当您需要访问受限的API时(通常在注册后),通常会采用这种方法。注册后,通常会向客户端颁发一个API密钥,然后可以存储该密钥并用于访问特定资源。颁发实体保留根据需要撤销和使API密钥失效的能力。

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

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

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

API密钥授权如何工作 典型的API密钥授权流程如下:

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

如上所述,服务器验证步骤可以由开发者按需实现。

示例 客户端发送的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"; // 通常来自存储/配置/数据库
            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、OAuth 2.0、基础认证和API密钥授权。理解这些方法至关重要,可以帮助您为特定的应用程序需求选择合适的方法。

相关留言评论
昵称:
邮箱:
阅读排行