在本文中,我们将探讨在 .NET API 中实现授权的四种常见方法:JWT(JSON Web 令牌)、OAuth、基本身份验证和 API 密钥授权。每种方法都有其优点和缺点,选择使用哪种方法取决于您的具体应用要求。授权对于保护 API 和确保只有经过身份验证和授权的用户才能访问特定资源至关重要。
JSON Web 令牌 (JWT) 是一种紧凑、URL 安全的方式,用于表示要在两方之间传输的声明。JWT 通常用于 Web 应用程序中的授权,允许服务器对用户进行身份验证并向他们颁发可用于后续请求的令牌。
令牌是一个字符串,由三部分组成:Header、Payload 和 Signature,并且它是自包含的,这意味着所有必要的信息(声明)都在令牌本身中编码。
JWT 是现代 Web 应用程序中基于令牌的无状态身份验证的绝佳选择。它们是独立的,允许跨分布式系统进行灵活的授权。但是,由于它们不可撤销,因此您必须确保令牌的生存期较短且可刷新。
典型的 JWT 身份验证流程包括:
第 1 步:安装所需的软件包
首先,我们需要安装一些处理 JWT 身份验证的 NuGet 包。
在 Package Manager 控制台中运行以下命令:
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
Install-Package Microsoft.IdentityModel.Tokens
Install-Package Microsoft.IdentityModel.JsonWebTokens
步骤 2:在 我们现在需要在文件中设置 JWT 身份验证。Program.cs
Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
//Define a secret key (ensure it's securely stored in production)
var key = "This is my custom Secret key for authentication";
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
// Configure token validation parameters
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true, // Token should expire at a specific time
ValidateIssuerSigningKey = true, // Validate the signature
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)) // Use the secret key to validate the token
};
});
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
//Enable authentication and authorization in the middleware pipeline
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
第 3 步:创建登录端点以生成 JWT 令牌
接下来,我们将创建一个身份验证控制器。此控制器将验证用户的凭据,并在成功登录后返回 JWT 令牌。
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";
}
// POST /api/auth/login
[HttpPost("login")]
public IActionResult Login([FromBody] UserLogin userLogin)
{
// Simple username/password check (in production, check against a database)
if (userLogin.Username == "test" && userLogin.Password == "password")
{
// Generate a JWT token for the user
var token = GenerateJwtToken(userLogin.Username);
return Ok(new { token });
}
// Return Unauthorized if credentials are wrong
return Unauthorized();
}
// Method to generate a JWT token
private string GenerateJwtToken(string username)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, username), // Subject claim (the username)
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) // Unique token ID
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_key)); // Create key from the secret
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); // Sign the token using HMAC SHA256
// Create the token
var token = new JwtSecurityToken(
issuer: null, // Not using issuer validation
audience: null, // Not using audience validation
claims: claims,
expires: DateTime.Now.AddMinutes(30), // Token expiration time
signingCredentials: creds // Include signing credentials
);
return new JwtSecurityTokenHandler().WriteToken(token); // Return the token as a string
}
}
// Model for accepting login requests
public class UserLogin
{
public string Username { get; set; }
public string Password { get; set; }
}
}
步骤 4:使用授权
保护终端节点设置 JWT 身份验证后,我们可以通过要求有效的令牌来访问 API 端点,从而保护它们。这是通过将属性添加到控制器或特定操作来完成的。[Authorize]
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[Route("api/[controller]")]
[ApiController]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
[Authorize] // This endpoint requires a valid token
public IActionResult Get()
{
return Ok(new { message = "This is a protected endpoint" });
}
}
通过执行这些步骤,您可以在 .NET API 中实现基于 JWT 的授权。JWT 允许安全、无状态的身份验证,并广泛用于现代应用程序。使用该属性,您可以轻松保护 API 的特定部分,确保只有经过身份验证的用户才能访问。[Authorize]
OAuth 是一个授权框架,它允许第三方服务在不暴露用户凭据的情况下交换令牌。OAuth 广泛用于保护 API,尤其是在允许用户使用 Google 或 Facebook 等第三方服务登录时。
在构建将与第三方服务交互的 API 或您需要允许用户使用外部提供商登录时,OAuth 是理想的选择。它对于用户委托的授权和单点登录 (SSO) 方案特别有用。与基本身份验证等传统方法相比,OAuth 提供了更高级别的安全性和灵活性。
OAuth 使用访问令牌授予对 API 的有限访问权限,而无需用户与应用程序共享其密码。一般流程包括:
OAuth 允许用户登录第三方应用程序,而无需创建单独的凭据。相反,用户通过使用 Google、GitHub 或 Facebook 等服务提供对其现有账户的访问权限来委派对这些应用程序的权限。
步骤 1:安装所需的 NuGet 包
首先,我们需要安装必要的软件包,以帮助我们处理 JWT 令牌和身份验证。
打开 Package Manager 控制台并运行
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
Install-Package Microsoft.IdentityModel.Tokens
Install-Package Microsoft.IdentityModel.JsonWebTokens
步骤 2:在 在您的文件中,我们需要配置身份验证服务以接受 JWT 令牌。以下代码设置身份验证,指定 JWT 作为默认方案,并配置 Token 验证参数,例如检查颁发者、受众和签名密钥。Program.cs
Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Secret key (Store securely in environment variables or a secret manager)
var key = "This is my custom Secret key for authentication";
// Configure Authentication with JWT
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true, // Ensures token was issued by the expected server
ValidateAudience = true, // Ensures token is intended for the correct audience
ValidateIssuerSigningKey = true, // Ensures token signature is correct
ValidIssuer = "https://localhost:7232", // Token issuer
ValidAudience = "https://localhost:7232", // Token audience
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)) // Signature key
};
});
builder.Services.AddAuthorization(); // Enables authorization based on roles/claims
builder.Services.AddControllers(); // Adds support for controllers and APIs
builder.Services.AddSwaggerGen(); // For Swagger API documentation
var app = builder.Build();
// Configure HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseRouting();
app.UseAuthentication(); // Ensures JWT authentication middleware is invoked
app.UseAuthorization(); // Ensures authorization policies are applied
app.MapControllers();
app.Run();
步骤 3:创建 Token 终端节点
接下来,我们将创建一个控制器,该控制器将充当我们的授权服务器。它将验证客户端凭证并颁发 JWT 令牌。
创建一个名为TokenController.cs
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
namespace MyApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TokenController : ControllerBase
{
private const string ClientId = "1"; // Static client_id (In production, retrieve from DB)
private const string ClientSecret = "secret"; // Static client_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)
{
// Validate client credentials
if (client_id == ClientId && client_secret == ClientSecret)
{
// Create token
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(Key);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] { new Claim("sub", client_id) }), // Token payload
Expires = DateTime.UtcNow.AddHours(1), // Token expiration time (1 hour)
Issuer = Issuer,
Audience = Audience,
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token); // Convert token to string
return Ok(new
{
access_token = tokenString, // Token to be used for future requests
token_type = "Bearer", // OAuth2 token type
expires_in = 3600 // Expiration time in seconds (1 hour)
});
}
return BadRequest("Invalid client credentials");
}
}
}
客户端可以使用此终端节点返回的令牌来访问受保护的资源。
步骤 4:使用 Token
保护资源现在我们已经实现了令牌生成,我们可以使用 JWT 令牌保护 API 终端节点。通过添加该属性,我们确保只有具有有效令牌的请求才能访问此终端节点。[Authorize]
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
namespace MyApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ResourceController : ControllerBase
{
[HttpGet]
[Authorize] // Protects this endpoint with JWT authorization
public IActionResult Get()
{
return Ok("Protected resource data"); // Only accessible with valid JWT
}
}
}
第 5 步:测试 OAuth 2.0 流程
使用 Postman 或 Curl 等工具将请求发送到带有 和 的终端节点。POST/tokenclient_idclient_secret
请求示例:
curl -X POST https://localhost:7232/api/token \
-F "client_id=1" \
-F "client_secret=secret"
这将返回一个访问令牌:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}
使用返回的令牌访问终端节点。/resource
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
https://localhost:7232/api/resource
如果令牌有效,您将收到包含受保护数据的响应
"Protected resource data"
此简化示例演示如何在 .NET API 中实现 OAuth 2.0 和 JWT 身份验证。通过使用令牌保护终端节点,我们确保只有授权用户或客户端才能访问敏感数据或功能。在生产系统中,请务必安全地存储密钥,实施适当的错误处理,并配置令牌过期和刷新机制,以实现最佳安全性和性能。
基本身份验证是一种更简单、安全性较低的方法,用于对 Web 资源实施访问控制。它内置于 HTTP 规范中,允许客户端通过发送以 base64 编码的用户名和密码来向服务器进行身份验证。
基本身份验证提供了一种非常简单的方法来保护 API,但只应在低安全性情况下使用。由于缺乏安全性和可扩展性,此方法不适用于生产环境。
基本身份验证应仅与 HTTPS 一起使用,以确保凭据在传输过程中加密。
步骤 1:创建身份验证处理程序
首先,我们需要创建自定义身份验证处理程序。此处理程序将负责验证 HTTP 标头中发送的凭据。Authorization
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;
// Custom Basic Authentication Handler
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()
{
// Check if the Authorization header is present
if (!Request.Headers.ContainsKey("Authorization"))
{
return AuthenticateResult.Fail("Missing Authorization Header");
}
try
{
// Parse the Authorization header
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 should be done against a user store)
if (username == "admin" && password == "password")
{
// Create claims and principal for the authenticated user
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");
}
}
}
第 2 步:注册身份验证处理程序
接下来,我们需要在应用程序的文件中注册我们的自定义身份验证处理程序。Program.cs
using Microsoft.AspNetCore.Authentication; // For authentication services
var builder = WebApplication.CreateBuilder(args);
// Add services to the container
builder.Services.AddControllers();
// Configure BasicAuthentication
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(); // Enable authentication
app.UseAuthorization(); // Enable authorization
app.MapControllers();
app.Run();
第 3 步:保护 API 终端节点
最后,我们可以使用 attribute 保护我们的 API 端点。下面是一个返回天气预报的控制器示例[Authorize]
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
// Secure the endpoint with [Authorize] attribute
[HttpGet]
[Authorize]
public IActionResult Get()
{
return Ok("Protected resource data");
}
}
此示例说明了如何在 .NET 应用程序中实现基本身份验证。虽然它对于测试来说既简单又有效,但在生产中使用它时要谨慎。始终优先考虑凭据的安全存储和传输,并根据应用程序的要求选择合适的身份验证方法。
API 密钥授权是一种简单的方法,通过向每个客户端颁发唯一密钥来控制对 API 的访问。客户端在其 API 请求中包含此密钥,服务器验证密钥以授予或拒绝访问权限。
API 密钥授权非常适合需要一种简单、低开销的方法来保护对 API 的访问的方案。它通常用于机器对机器通信或与外部服务集成时。
第 1 步:创建 API 密钥授权中间件
中间件将通过检查请求是否包含有效的 API 密钥来处理授权。
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)
{
// Check if the request contains the API key in the headers
if (!context.Request.Headers.TryGetValue(API_KEY_HEADER_NAME, out var extractedApiKey))
{
context.Response.StatusCode = 401; // Unauthorized
await context.Response.WriteAsync("API Key is missing.");
return;
}
// Define the correct API key (this should be stored securely)
var correctApiKey = "SuperSecret";
// Compare the extracted key with the correct one
if (!correctApiKey.Equals(extractedApiKey))
{
context.Response.StatusCode = 401; // Unauthorized
await context.Response.WriteAsync("Unauthorized client.");
return;
}
// Call the next middleware in the pipeline
await _next(context);
}
}
第 2 步:注册 Middleware
要在应用程序中使用中间件,您需要在 中注册它。Program.cs
using APIKey.Middleware; // Assuming the middleware is inside this namespace
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); // Add controller support
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); // Enable Swagger for API documentation
var app = builder.Build();
// Register the API Key Middleware
app.UseMiddleware<ApiKeyMiddleware>();
// Enable Swagger for development
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseRouting();
app.MapControllers();
app.Run();
第 3 步:保护端点
注册中间件后,您的 API 控制器将自动需要 API 密钥。
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok("This is a protected resource, accessible with a valid API key.");
}
}
此实现演示了一个基本且易于理解的 API 密钥授权系统。在生产设置中,API 密钥通常存储在更安全的位置,例如配置文件、数据库或环境变量,而不是硬编码。
四种授权方法(JWT、OAuth、基本身份验证和 API 密钥授权)中的每一种都有自己的优势和用例。虽然 JWT 和 OAuth 为现代应用程序提供了更强大、更安全的方法,但基本身份验证和 API 密钥可用于更简单的使用案例。了解每种方法的优点和局限性将有助于您根据特定应用程序需求做出最佳决策。