在构建现代 restful api 时,安全性是不可或缺的一环。通常,这涉及使用外部授权服务器(如 aws cognito、asgardeo 或其他符合 oauth 2.0/openid connect 规范的服务)来颁发和验证访问令牌。对于许多后端开发者而言,核心需求是构建一个“资源服务器”,它能够验证每个传入请求中的 authorization 头(通常包含 bearer 访问令牌),并根据令牌的有效性决定是否允许访问。
在 Spring Boot 等框架中,通过引入特定的 starter 依赖并进行少量配置,即可轻松实现这种纯资源服务器模式。然而,对于刚接触 C# .NET 的开发者,可能会发现相关教程往往与用户管理紧密耦合,难以找到一个简洁的、专注于令牌验证的解决方案。本教程将展示如何在 C# .NET 应用程序中,以最少的配置实现这一目标。
核心实现步骤本解决方案基于 .NET 6.0 或更高版本,利用 ASP.NET Core 内置的认证和授权机制。
1. 配置 JWT Bearer 认证服务首先,在应用程序的 Program.cs 文件中(或 Startup.cs 的 ConfigureServices 方法中),需要添加认证服务并指定 JWT Bearer 认证方案。这是配置外部授权服务器集成最关键的一步。
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; var builder = WebApplication.CreateBuilder(args); // 添加认证服务 builder.Services.AddAuthentication(options => { // 将 JWT Bearer 设置为默认的认证和挑战方案 options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { // 配置 JWT Bearer 选项 // Audience (受众) 是验证令牌的接收方,通常是你的客户端ID或API标识符 options.Audience = "<client_id_或_API_标识符>"; // MetadataAddress (元数据地址) 是OpenID Connect发现端点, // JWT Bearer 中间件将从此端点自动获取公钥(JWKS)、颁发者(Issuer)等信息 options.MetadataAddress = "https://api.asgardeo.io/t/<app_name>/oauth2/token/.well-known/openid-configuration"; // 如果你的授权服务器不提供标准的.well-known/openid-configuration, // 或者需要更精细的控制,你可以手动指定Issuer和IssuerSigningKeyResolver等。 // 例如: // options.Authority = "https://api.asgardeo.io/t/<app_name>/oauth2/token"; // 颁发者地址 // options.TokenValidationParameters = new TokenValidationParameters // { // ValidateIssuer = true, // ValidIssuer = "https://api.asgardeo.io/t/<app_name>/oauth2/token", // ValidateAudience = true, // ValidAudience = "<client_id_或_API_标识符>", // ValidateLifetime = true, // ValidateIssuerSigningKey = true, // // IssuerSigningKeyResolver 可以用于动态获取签名密钥 // // IssuerSigningKeyResolver = (token, securityToken, kid, parameters) => ... // }; }); // 添加控制器服务 builder.Services.AddControllers(); // ... 其他服务配置
关键配置项解释:
- options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;: 指定当需要对请求进行认证时,默认使用 JWT Bearer 方案。
- options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;: 指定当认证失败(例如,令牌无效或缺失)时,默认使用 JWT Bearer 方案来发送挑战(通常是返回 401 Unauthorized 响应)。
- options.Audience = "<client_id_或_API_标识符>";: 这是令牌的“受众”声明(aud claim)。JWT Bearer 中间件会验证传入令牌中的 aud 声明是否与此配置值匹配。这确保了令牌是为你当前的 API 颁发的。
- options.MetadataAddress = "https://api.asgardeo.io/t/<app_name>/oauth2/token/.well-known/openid-configuration";: 这是 OpenID Connect 发现文档的 URL。JWT Bearer 中间件会访问此 URL,自动获取授权服务器的公钥(JWKS)、颁发者(iss claim)等信息,用于验证令牌的签名和颁发者。这种方式极大地简化了配置,避免了手动下载和管理公钥。
配置完服务后,需要在应用程序的 HTTP 请求处理管道中启用认证和授权中间件。这些中间件必须按照正确的顺序添加。
var app = builder.Build(); // ... 其他中间件配置 (如 UseHttpsRedirection, UseRouting等) // 必须在 UseRouting 之后、UseEndpoints 之前调用 app.UseAuthentication(); // 启用认证中间件,负责解析请求中的认证信息(如JWT令牌) app.UseAuthorization(); // 启用授权中间件,根据认证信息决定是否允许访问资源 app.MapControllers(); // 将控制器映射到路由 app.Run();
顺序的重要性:
- UseAuthentication() 必须在 UseRouting() 之后,因为它需要访问路由信息来决定哪些端点需要认证。
- UseAuthorization() 必须在 UseAuthentication() 之后,因为它依赖于认证中间件提供的用户身份信息来执行授权检查。
- MapControllers()(或 UseEndpoints)必须在 UseAuthorization() 之后,以确保授权规则在端点被执行前生效。
最后一步是在控制器或具体的 Action 方法上应用 [Authorize] 属性。这会指示 ASP.NET Core 要求请求必须经过认证和授权才能访问该端点。
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; [ApiController] [Route("[controller]")] public class MySecuredController : ControllerBase { [HttpGet] [Route("Private")] [Authorize] // 标记此Action方法需要授权 public IActionResult Private() { // 如果请求到达这里,说明已经成功通过认证和授权 return Ok(new { Message = "Hello from a private endpoint. You are authorized!" }); } [HttpGet] [Route("Public")] public IActionResult Public() { // 此Action方法无需授权即可访问 return Ok(new { Message = "Hello from a public endpoint." }); } }
当客户端向 /MySecured/Private 端点发送请求时,如果请求头中不包含有效的 Bearer 令牌,或者令牌无效,ASP.NET Core 将返回 401 Unauthorized 状态码。如果令牌有效,请求将正常处理。
注意事项与进阶- 无客户端密钥场景: 本教程提供的解决方案主要适用于纯资源服务器场景,即 API 本身不持有客户端密钥去与授权服务器进行令牌交换,而是直接验证收到的访问令牌。这种模式通常用于公共客户端(如 SPA 或移动应用)向后端 API 发送请求的情况。
- 令牌验证参数: MetadataAddress 会自动处理大部分令牌验证参数(如 ValidateIssuer、ValidateIssuerSigningKey)。如果你的授权服务器不遵循标准的 OpenID Connect 发现协议,或者你需要更精细的控制,可以手动配置 TokenValidationParameters,例如指定 ValidIssuer、ValidAudiences、IssuerSigningKey 等。
- Scope 验证: 如果需要基于令牌中的 scope 声明进行更细粒度的授权,可以创建自定义授权策略,例如 [Authorize(Policy = "RequireAdminScope")]。
- 错误处理: 对于认证和授权失败,可以通过自定义 JwtBearerEvents 来拦截和处理,例如记录日志或返回自定义的错误响应。
- CORS 配置: 如果你的前端应用与 API 部署在不同的域,请确保正确配置了 CORS (Cross-Origin Resource Sharing) 策略。
通过上述步骤,你可以在 C# .NET 应用程序中轻松实现一个纯资源服务器,利用外部授权服务器的 JWT Bearer 令牌来保护你的 REST API。这种方法简洁高效,避免了不必要的复杂性,使得开发者能够专注于业务逻辑,同时确保 API 的安全性。这种配置方式与 Spring Boot 中使用 spring-boot-starter-oauth2-resource-server 的体验非常相似,提供了极简的集成方案。
以上就是在 C# .NET 中使用外部授权服务器保护 REST API:极简配置指南的详细内容,更多请关注知识资源分享宝库其它相关文章!
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。