opencms 做的网站,免费wordpress外贸主题,网站建站 在线制作,免费做网站的网址有哪些在微服务架构中#xff0c;通过在网关层实现身份认证、权限校验和数据加密#xff0c;可以有效防范恶意攻击和非法访问#xff0c;保障内部服务安全。采用JWT、OAuth等主流认证机制#xff0c;使每次请求均经过严格验证#xff0c;降低安全漏洞风险。同时#xff0c;统一…在微服务架构中通过在网关层实现身份认证、权限校验和数据加密可以有效防范恶意攻击和非法访问保障内部服务安全。采用JWT、OAuth等主流认证机制使每次请求均经过严格验证降低安全漏洞风险。同时统一的安全管理还便于监控和日志审计及时发现异常行为并做出响应。完善安全策略不仅增强系统稳定性还能构建可靠访问环境切实保护企业数据安全。
一、JWT认证详解
1.1 JWT简介与工作原理
JSON Web TokenJWT是一种开放标准RFC 7519用于在网络环境中以紧凑、自包含的方式安全传递信息。它基于JSON格式并通过数字签名保证数据完整性与真实性广泛应用于身份验证和信息交换。JWT由三部分组成头部Header、载荷Payload和签名Signature并以“.”分隔。头部包含令牌类型及签名算法如HS256或RSA经过Base64Url编码后形成第一部分。载荷存储标准声明如发行者 iss、过期时间 exp、主题 sub和自定义数据用于传递身份信息但未加密不应存敏感数据。签名部分使用密钥对前两部分加密确保数据未被篡改。 JWT的主要优势是无状态无需服务器存储会话信息适用于分布式系统和负载均衡。其轻量特性使其易于在HTTP请求头中传输同时基于JSON格式具备跨平台兼容性方便不同系统统一认证。JWT常与OAuth2、OpenID Connect结合用于安全授权并内置过期机制exp防止令牌滥用。在微服务架构中各服务节点只需验证JWT即可判定请求合法性提升系统扩展性。 使用JWT时需确保密钥安全防止令牌泄露并选择合适的签名算法如HS256或RS256。可结合令牌黑名单、短生命周期策略等增强安全性防止令牌被截获后重放。
1.2 JWT在Ocelot中的应用
在Ocelot网关中JWT实现了高效的用户身份认证与授权。客户端在向网关发起请求时会在HTTP请求头中携带JWT格式为“Bearer {token}”。Ocelot作为统一入口会拦截请求并通过认证中间件解析、验证令牌包括检查令牌的存在性、格式、签名、过期时间等。验证成功后Ocelot将请求转发至后端微服务否则返回未授权响应。这种机制确保只有合法认证的请求能进入系统同时便于集中管理和安全监控。 JWT的生成与验证通常由认证服务器如IdentityServer负责。当用户成功认证后服务器使用指定算法如HS256或RS256生成JWT其中包含头部类型与算法、载荷用户标识、权限、过期时间等和签名用于数据完整性保护。生成的JWT返回给客户端并在后续请求中携带。Ocelot接收请求后通过密钥或公钥验证JWT解码并校验签名及过期时间确保请求合法。一旦通过网关允许请求进入微服务网络从而实现安全、高效的身份认证管理。
二、使用IdentityServer进行认证
2.1 IdentityServer简介
IdentityServer是一个基于.NET的开源身份认证与授权框架主要用于单点登录、API保护和安全令牌管理。它帮助开发者在微服务架构中构建统一的认证中心简化身份验证和授权流程。 作为安全令牌服务STSIdentityServer生成JWT等令牌在微服务间传递用户信息和权限。它支持单点登录SSO避免重复认证并允许开发者灵活配置客户端、API资源和授权范围。其支持授权码、隐式、混合、客户端凭据、资源所有者密码等多种认证流程并内置令牌管理、刷新、撤销等安全机制确保认证体系安全稳定。 IdentityServer基于OAuth2和OpenID Connect协议OAuth2负责授权允许第三方应用安全访问资源而OIDC扩展OAuth2引入ID Token实现用户身份验证。IdentityServer提供访问令牌管理并支持用户信息查询接口确保跨系统认证的便捷性和安全性。通过标准化流程IdentityServer提升了客户端兼容性简化系统集成使企业能够构建高效、可信的身份认证体系。
2.2 IdentityServer与JWT认证的关系
IdentityServer与JWT认证关系密切它通过JWTJSON Web Token作为身份验证和授权的核心机制为分布式系统提供安全、无状态的令牌管理。IdentityServer作为OAuth2和OpenID Connect的实现负责用户身份验证并在授权后颁发JWT令牌供客户端访问受保护资源时使用。 在身份认证过程中用户通过IdentityServer验证身份后服务器会生成包含用户信息、权限范围等数据的JWT并返回给客户端。客户端在后续请求中将该JWT附加到HTTP请求头中API网关或后端服务通过验证JWT签名和有效期确保请求合法性和安全性。 JWT的无状态特性使得服务端无需存储会话信息提升了系统的扩展性。IdentityServer通过OAuth2支持JWT的生成、刷新和撤销使得身份认证和授权更加标准化、安全和高效。综合而言IdentityServer利用JWT实现安全的身份认证和授权管理确保微服务架构下的高效通信与访问控制。
2.3 集成步骤
在Ocelot网关中集成IdentityServer主要涉及身份认证与授权配置确保所有经过网关的请求都需要进行身份验证。 搭建IdentityServer 在独立的认证服务器上搭建 IdentityServer4配置客户端、API资源、授权范围Scopes确保IdentityServer能够正确颁发 JWT令牌并支持OAuth2或OpenID Connect协议。 配置Ocelot网关 在ocelot.json配置文件中配置要网关路由相关内容已经在ocelot 路由文章中已经讲解了这里就不在重复讲解。 在Ocelot中启用认证 首先在 Program.cs 中添加JWT身份验证 builder.Services.AddAuthentication(Bearer).AddJwtBearer(Bearer, options {options.Authority http://localhost:5000; // IdentityServer地址options.RequireHttpsMetadata false;options.Audience api1; // 资源API});然后注册Ocelot网关 builder.Services.AddOcelot();三、案例演示
我们根据上一小节的Ocelot网关中集成IdentityServer的步骤来实现一个简单的例子。 新建解决方案OcelotIdentityServerDemo并在这个解决方案下新建三个Web Api类库Weather、Authentication和Gateway。Weather是业务服务用来获取天气Authentication作为认证服务器使用Gateway作为网关使用。
3.1 Authentication 认证服务 引入IdentityServer包 打开命令行切换到 Authentication Web Api 类库所在文件夹执行如下命令 dotnet add package OpenIddict.AspNetCore
dotnet add package OpenIddict.EntityFrameworkCore
dotnet add package OpenIddict.Validation.AspNetCore在上面的命令中我们在类库中添加了OpenIddict.AspNetCore、OpenIddict.EntityFrameworkCore和OpenIddict.Validation.AspNetCore。这三个包的作用如下 OpenIddict.AspNetCore 这个包将 OpenIddict 与 ASP.NET Core 无缝集成提供构建认证服务器所需的各类终结点如 token 颁发端点和中间件支持。它简化了配置授权流程、处理请求、签发和撤销令牌等工作使开发者能快速搭建符合 OAuth 2.0 和 OpenID Connect 标准的认证服务。OpenIddict.EntityFrameworkCore 该包利用 Entity Framework Core 作为 OpenIddict 的数据存储解决方案。它提供了必要的实体映射和数据库上下文配置用于持久化应用信息、授权记录、令牌数据等安全相关的数据。通过这一集成开发者可以方便地管理和查询认证数据同时享受 EF Core 提供的数据迁移和操作功能。OpenIddict.Validation.AspNetCore 这个包主要用于在 ASP.NET Core 环境中对由 OpenIddict 颁发的令牌进行验证。它将 OpenIddict 的令牌验证逻辑集成到 ASP.NET Core 的身份认证中间件中确保每个访问受保护资源的请求都能自动校验令牌的有效性和完整性从而保障 API 的安全访问。 配置Authentication服务 在 Program.cs中添加 OpenIddict 服务代码如下 builder.Services.AddOpenIddict().AddCore(options {// 使用 EntityFrameworkCore作为数据源options.UseEntityFrameworkCore().UseDbContextIdentityServerDbContext();}).AddServer(options {// 设置令牌端点options.SetTokenEndpointUris(/connect/token);// 启用密码模式options.AllowPasswordFlow() // 开启密码模式.AllowClientCredentialsFlow().AllowRefreshTokenFlow();// 开启刷新令牌options.RegisterScopes(api);// 使用开发环境下的临时密钥生产环境请使用持久化证书options.AddDevelopmentEncryptionCertificate().AddDevelopmentSigningCertificate();options.AddSigningKey(new SymmetricSecurityKey(Convert.FromBase64String(SguGSpvaRLMwnmnxiBHRdSxRpBDSiD88J1qp1czuD8)));options.AddEncryptionKey(new SymmetricSecurityKey(Convert.FromBase64String(na8LnVekSu5b3fgdUhyoKuLTMVGYLtgHrTTpKCB5VY)));// 集成 ASP.NET Coreoptions.UseAspNetCore().EnableAuthorizationEndpointPassthrough().EnableTokenEndpointPassthrough().DisableTransportSecurityRequirement();// 开发模式下金庸HTTPS}).AddValidation(options {// 使用本地服务器进行验证options.UseLocalServer();// 使用 AspNetCore 进行验证options.UseAspNetCore();});builder.Services.AddAuthentication(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);这段代码利用 OpenIddict 在 ASP.NET Core 应用中构建一个完整的 OAuth 2.0 授权和资源管理体系。它分为几个关键部分每个部分都负责不同的功能共同协作以实现安全可靠的身份验证和授权。 首先builder.Services.AddOpenIddict() 是整个配置的起点。它像一个基石将 OpenIddict 的核心服务注册到 ASP.NET Core 的依赖注入容器中。这使得应用能够使用 OpenIddict 提供的各种功能比如定义客户端、管理授权、颁发和验证令牌等。可以把它想象成激活了 OpenIddict 的总开关后续的配置都是在此基础上进行的。 接下来.AddCore(options { ... }) 用于配置 OpenIddict 的核心数据存储。这部分代码指定了使用 Entity Framework Core 作为 OpenIddict 的数据持久化方案。options.UseEntityFrameworkCore() 这行代码告诉 OpenIddict所有关于客户端、授权、Scope 和令牌的信息都将存储在数据库中。这个设置对于生产环境至关重要因为它可以确保数据在应用重启后仍然存在。options.UseDbContextIdentityServerDbContext() 则进一步指定了用于 EF Core 的 DbContext。IdentityServerDbContext 必须是一个继承自 DbContext 的类并且包含 OpenIddict 所需的实体比如 OpenIddictEntityFrameworkCoreApplication客户端信息、OpenIddictEntityFrameworkCoreAuthorization授权信息、OpenIddictEntityFrameworkCoreScope (Scope 信息) 和 OpenIddictEntityFrameworkCoreToken令牌信息。我们需要根据数据库结构来定义这个 DbContext并确保它与 OpenIddict 的期望相符。 .AddServer(options { ... }) 是配置 OAuth 2.0 授权服务器的关键部分。它定义了授权服务器的行为和端点。options.SetTokenEndpointUris(/connect/token) 设置了令牌端点的 URI客户端会向这个端点发送请求以获取访问令牌。可以把这个端点看作是授权服务器的核心入口所有令牌相关的请求都会经过这里。options.AllowPasswordFlow()、.AllowClientCredentialsFlow() 和 .AllowRefreshTokenFlow() 分别启用了 OAuth 2.0 规范中定义的几种授权模式密码模式、客户端凭据模式和刷新令牌模式。密码模式允许客户端直接使用用户的用户名和密码来获取令牌客户端凭据模式允许客户端使用自己的身份信息客户端 ID 和密钥来获取令牌而刷新令牌模式则允许客户端使用刷新令牌来获取新的访问令牌避免用户重复登录。 Tip需要注意的是密码模式在安全性方面有一些争议通常不建议在生产环境中使用除非有充分的理由并且采取了额外的安全措施。 options.RegisterScopes(api) 注册了一个名为 “api” 的 ScopeScope 用于限制访问令牌的权限。客户端在请求令牌时可以指定所需的 Scope授权服务器会根据这些 Scope 来生成具有相应权限的令牌。options.AddDevelopmentEncryptionCertificate().AddDevelopmentSigningCertificate() 在开发环境中配置了用于加密和签名令牌的证书。这些方法添加了临时的、自动生成的证书方便开发和测试。然而在生产环境中绝对不能使用这种方式必须使用由受信任的证书颁发机构颁发的证书并妥善保管私钥。options.AddSigningKey(...) 和 options.AddEncryptionKey(...) 使用对称密钥来签名和加密令牌。 Tip这里直接在代码中硬编码了密钥这是一种非常不安全的做法绝对不能在生产环境中使用。在生产环境中应该使用更安全的密钥管理方案比如使用 Azure Key Vault 或 HashiCorp Vault 等专门的密钥管理服务来存储和管理密钥。 options.UseAspNetCore() 将 OpenIddict 与 ASP.NET Core 集成使得 OpenIddict 可以利用 ASP.NET Core 的各种功能比如路由、中间件和依赖注入。.EnableAuthorizationEndpointPassthrough() 和 .EnableTokenEndpointPassthrough() 启用了授权和令牌端点的直通模式我们可以使用 ASP.NET Core 的路由机制来处理这些端点而不是完全依赖 OpenIddict 的默认处理方式。.DisableTransportSecurityRequirement() 禁用了 HTTPS 传输安全要求。这只应该在开发环境中进行因为在生产环境中必须使用 HTTPS 来保护敏感数据的传输。 .AddValidation(options { ... }) 配置了 OpenIddict 的令牌验证功能。这部分代码定义了 API 资源服务器如何验证客户端提供的访问令牌。options.UseLocalServer() 配置使用本地授权服务器进行令牌验证API 资源服务器会与同一应用中的授权服务器通信来验证令牌的有效性。通常用于单体应用或微服务架构中的内部服务之间的验证。 最后builder.Services.AddAuthentication(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme) 将 OpenIddict 的验证方案添加到 ASP.NET Core 的身份验证管道中。可以使用 ASP.NET Core 的身份验证机制来保护 API 资源只有持有有效 OpenIddict 令牌的客户端才能访问这些资源。 配置数据库上下文 在上一小节中 我们指定了数据库上下文 IdentityServerDbContext在这一小节中我们就来创建这个数据库上下文。 首先安装数据库提供程序Pomelo.EntityFrameworkCore.MySql在命令行输入如下命令 dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Pomelo.EntityFrameworkCore.MySql然后创建自定义 DbContext IdentityServerDbContext重写 OnModelCreating 方法其中调用 builder.UseOpenIddict() 注册 OpenIddict 的实体映射重写OnConfiguring方法配置数据库连接。代码如下 using Microsoft.EntityFrameworkCore;namespace Authentication;
/// summary
/// IdentityServer 数据库上下文
/// /summary
public class IdentityServerDbContext : DbContext
{/// summary/// 数据库连接配置/// /summaryIConfiguration _dbConfig;/// summary/// 构造函数/// /summary/// param namedbConfig/parampublic IdentityServerDbContext(IConfiguration dbConfig) {_dbConfig dbConfig;}/// summary/// 配置数据库模型/// /summary/// param namemodelBuilder/paramprotected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);// 配置 OpenIddictmodelBuilder.UseOpenIddict();}/// summary/// 数据库连接配置/// /summary/// param nameoptionsBuilder/paramprotected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){var serverVersion ServerVersion.AutoDetect(_dbConfig.GetConnectionString(MySQLConnection));optionsBuilder.UseMySql(_dbConfig.GetConnectionString(MySQLConnection), serverVersion);}
}配置数据库链接字符串 打开appsettings.json配置文件添加数据库连接字符串代码如下 ConnectionStrings: {MySQLConnection: server14.103.224.141;port3308;databaseIdentityServerDemo;userroot;pwd123*asdasd;
}数据库迁移 所有配置完成后使用 Entity Framework Core 的迁移命令生成数据库结构这将创建 OpenIddict 所需的表结构。 dotnet ef migrations add InitialMigration
dotnet ef database update设置种子数据 在Authentication项目根目录下新建SeedData类这个类继承子IHostedService在里面编写种子数据的代码。 public class SeedData: IHostedService
{private readonly IServiceProvider _serviceProvider;public SeedData(IServiceProvider serviceProvider){_serviceProvider serviceProvider;}public async Task StartAsync(CancellationToken cancellationToken){using var scope _serviceProvider.CreateScope();var applicationManager scope.ServiceProvider.GetRequiredServiceIOpenIddictApplicationManager();// 检查是否存在指定的 client_idif (await applicationManager.FindByClientIdAsync(client_id1, cancellationToken) is null){var descriptor new OpenIddictApplicationDescriptor{ClientId client_id1,ClientSecret client_secret, // 根据需求设置密钥DisplayName 示例客户端应用,Permissions {OpenIddictConstants.Permissions.Endpoints.Token,OpenIddictConstants.Permissions.GrantTypes.Password, // 允许密码模式OpenIddictConstants.Permissions.GrantTypes.ClientCredentials,OpenIddictConstants.Permissions.Prefixes.Scope api // 允许访问的 API 作用域}};await applicationManager.CreateAsync(descriptor, cancellationToken);}}public Task StopAsync(CancellationToken cancellationToken) Task.CompletedTask;
}这段代码在 ASP.NET Core 中使用 OpenIddict 配置 OpenID Connect 客户端SeedData 实现了 IHostedService在应用程序启动时创建客户端配置。通过构造函数注入 IServiceProvider在 StartAsync 中创建服务范围解析 IOpenIddictApplicationManager并检查是否存在指定的 ClientId。 如果不存在创建 OpenIddictApplicationDescriptor 定义客户端配置包括 ClientId客户端 ID、ClientSecret密钥、DisplayName显示名称和权限。权限包括访问 Token 端点Endpoints.Token、密码模式授权GrantTypes.Password、客户端凭据授权GrantTypes.ClientCredentials和访问名为 api 的作用域。通过 applicationManager.CreateAsync() 将配置存储在 OpenIddict 的配置存储中。StopAsync 为空不需要在程序关闭时执行清理。 接着将SeedData类引入到Program类中 builder.Services.AddHostedServiceSeedData();实现令牌端点 在Program类中设置了获取令牌的端点在这一小节就来实现这个端点。首先创建AuthorizationController控制器接着在其中编写令牌端点的代码。 using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using OpenIddict.Abstractions;
using OpenIddict.Server.AspNetCore;
using System.Security.Claims;
using Microsoft.AspNetCore;namespace Authentication.Controllers
{/// summary/// 授权控制器/// /summary[Route(/connect)][ApiController]public class AuthorizationController : ControllerBase{/// summary/// 令牌端点/// /summary/// returns/returns/// exception crefInvalidOperationException/exception[HttpPost(token)]public IActionResult Exchange(){var request HttpContext.GetOpenIddictServerRequest();// 处理资源所有者密码模式if (request.IsPasswordGrantType()){// 示例仅支持用户名 alice 和密码 password123if (request.Username ! alice || request.Password ! password123){return Forbid(authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,properties: new AuthenticationProperties(new Dictionarystring, string?{[OpenIddictServerAspNetCoreConstants.Properties.Error] OpenIddictConstants.Errors.InvalidGrant,[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] 用户名或密码错误。}));}// 创建用户身份并添加必要的声明var identity new ClaimsIdentity(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);identity.AddClaim(OpenIddictConstants.Claims.Subject, request.Username);identity.AddClaim(OpenIddictConstants.Claims.Name, Alice);identity.AddClaim(OpenIddictConstants.Claims.Audience, api); // 添加 aud 声明// 创建 ClaimsPrincipal并设置请求的范围var principal new ClaimsPrincipal(identity);principal.SetScopes(request.GetScopes());// 返回 SignIn 结果OpenIddict 将生成访问令牌return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);}// 处理客户端凭证模式else if (request.IsClientCredentialsGrantType()){// 客户端凭证模式下客户端身份已由 OpenIddict 验证器验证直接使用 client_id 作为主题标识var identity new ClaimsIdentity(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);identity.AddClaim(OpenIddictConstants.Claims.Subject,request.ClientId ?? throw new InvalidOperationException());identity.AddClaim(OpenIddictConstants.Claims.Audience, api); // 添加 aud 声明var principal new ClaimsPrincipal(identity);principal.SetScopes(request.GetScopes());return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);}// 不支持的授权类型else{return BadRequest(new{error OpenIddictConstants.Errors.UnsupportedGrantType,error_description 不支持的授权类型。});}}}
}Exchange 方法用来处理客户端通过 OpenID Connect 发起的令牌请求。首先通过 HttpContext.GetOpenIddictServerRequest() 获取当前的 OpenIddict 请求对象 request。这个对象封装了 OpenID Connect 协议中的授权请求参数例如授权类型grant_type、客户端 IDclient_id、作用域scope、用户名username和密码password等。接下来代码根据授权类型grant_type来决定如何处理授权请求。如果授权类型是“资源所有者密码模式”Password Grant通过 request.IsPasswordGrantType() 进行判断。 在密码模式下客户端需要提供用户名和密码来直接获取访问令牌。 在这里代码接受用户名为 “alice” 且密码为 “password123” 的请求。 如果用户名或密码不正确返回 Forbid() 结果。Forbid() 方法表示认证失败使用 OpenIddictServerAspNetCoreDefaults.AuthenticationScheme 作为认证方案同时设置 OpenIddict 定义的错误代码和错误描述。AuthenticationProperties 被用来传递错误信息其中 OpenIddictServerAspNetCoreConstants.Properties.Error 被设置为 invalid_grant表示授权失败OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription 设置为 “用户名或密码错误。”。 如果用户名和密码正确创建一个 ClaimsIdentity 对象使用 OpenIddictServerAspNetCoreDefaults.AuthenticationScheme 作为身份验证方案。然后通过 identity.AddClaim() 方法为身份对象添加声明Claim - OpenIddictConstants.Claims.Subject设置 sub主题声明表示身份的唯一标识这里设置为用户名 alice。 - OpenIddictConstants.Claims.Name设置 name 声明表示用户的显示名称设置为 Alice。 - OpenIddictConstants.Claims.Audience设置 aud受众声明表示访问的资源设置为 api。 然后创建 ClaimsPrincipal 对象将前面创建的 ClaimsIdentity 作为参数传入。通过 principal.SetScopes(request.GetScopes()) 方法设置请求的作用域scopeOpenIddict 将根据这些作用域生成访问令牌中包含的权限范围。 最后调用 SignIn() 方法传入 principal 和 OpenIddictServerAspNetCoreDefaults.AuthenticationScheme 作为参数。SignIn() 方法表示认证成功OpenIddict 将根据传入的声明信息生成访问令牌并返回给客户端。 如果授权类型是“客户端凭证模式”Client Credentials Grant则通过 request.IsClientCredentialsGrantType() 进行判断。在客户端凭证模式下客户端不涉及用户身份直接通过 client_id 和 client_secret 进行认证。由于 OpenIddict 的验证器已经在此之前完成了对 client_id 和 client_secret 的验证因此代码直接创建一个新的 ClaimsIdentity 对象并通过 identity.AddClaim() 方法添加声明 - OpenIddictConstants.Claims.Subject设置 sub主题声明表示客户端身份设置为 request.ClientId。如果 request.ClientId 为空抛出 InvalidOperationException。 - OpenIddictConstants.Claims.Audience设置 aud受众声明表示访问的资源设置为 api。 最后创建 ClaimsPrincipal并设置请求的作用域。调用 SignIn() 方法OpenIddict 将生成访问令牌返回给客户端。 这段代码密码模式主要用于涉及用户身份的场景如通过用户名和密码登录获取用户授权的访问令牌。而客户端凭证模式用于服务间通信不涉及用户身份仅通过客户端凭据授权。
3.2 Gateway 服务 网关配置 命令行切换到 Gateway Web Api 类库所在文件夹将网关Ocelot引入进来 dotnet add package Ocelot在项目中 Program.cs 中配置 JWT Bearer 认证将 Authority 指向 OpenIddict 身份认证服务器地址并注册 Ocelot // 配置 JWT 认证验证来自认证服务器的令牌
builder.Services.AddAuthentication(Bearer).AddJwtBearer(Bearer, options {options.Authority https://localhost:5002; // 认证服务器地址options.RequireHttpsMetadata false; // 开发环境下允许 HTTPoptions.Audience api; // 与业务服务器要求的受众一致options.TokenValidationParameters new TokenValidationParameters{ValidateIssuer false,ValidateAudience false,ValidateLifetime false,ValidateIssuerSigningKey false, // 忽略 kid 检查ValidAudience api, ValidIssuer http://localhost:5002,IssuerSigningKey new SymmetricSecurityKey(Convert.FromBase64String(SguGSpvaRLMwnmnxiBHRdSxRpBDSiD88J1qp1czuD8)),TokenDecryptionKey new SymmetricSecurityKey(Convert.FromBase64String(na8LnVekSu5b3fgdUhyoKuLTMVGYLtgHrTTpKCB5VY)),ValidAlgorithms new[]{SecurityAlgorithms.HmacSha256,SecurityAlgorithms.Aes256KW,SecurityAlgorithms.Aes256CbcHmacSha512}};});// 注册 Ocelot 服务
builder.Services.AddOcelot();这段代码配置了JWT认证并注册了Ocelot服务。首先builder.Services.AddAuthentication(Bearer).AddJwtBearer(Bearer, options { ... });这部分用于添加Bearer类型的JWT认证。options.Authority https://localhost:5002; 设置了认证服务器的地址表明token是从这个地址签发的。options.RequireHttpsMetadata false; 在开发环境下允许使用HTTP正式环境应该设置为true以保证安全性。options.Audience api;指定了业务服务器期望的受众确保只有目标为 “api” 的token才被接受。options.TokenValidationParameters 定义了token验证的各种参数其中ValidateIssuer false, ValidateAudience false, ValidateLifetime false, ValidateIssuerSigningKey false 这些参数都被设置为false这意味着发行者、受众、生命周期和签名密钥的验证都被禁用了这通常不建议在生产环境中使用。ValidAudience api,ValidIssuer http://localhost:5002 指定了合法的受众和发行者但由于前面的验证参数被禁用这些设置实际上没有生效。IssuerSigningKey 和 TokenDecryptionKey 使用了对称密钥这些密钥从Base64字符串转换而来用于签名验证和token解密。ValidAlgorithms 指定了允许的加密算法包括HmacSha256, Aes256KW 和 Aes256CbcHmacSha512。最后builder.Services.AddOcelot();注册了Ocelot服务Ocelot是一个API网关用于路由和管理API请求。总的来说这段代码配置了一个JWT认证方案但由于禁用了许多关键验证步骤可能存在安全风险并注册了Ocelot API网关服务。 配置路由 接着进行Ocelot 网关配置在项目的 ocelot.json 文件中配置路由和认证信息。假设所有请求通过 Ocelot 转发至下游 API并要求 JWT 验证 {Routes: [{DownstreamPathTemplate: /connect/token,DownstreamScheme: http,DownstreamHostAndPorts: [{Host: localhost,Port: 5002}],UpstreamPathTemplate: /connect/token,UpstreamHttpMethod: [ POST ]},{DownstreamPathTemplate: /api/{everything},DownstreamScheme: http,DownstreamHostAndPorts: [{Host: localhost,Port: 5001}],UpstreamPathTemplate: /api/{everything},UpstreamHttpMethod: [ GET, POST, PUT, DELETE ],AuthenticationOptions: {AuthenticationProviderKey: Bearer,AllowedScopes: [ api ]},AddHeadersToRequest: {Authorization: Authorization}}],GlobalConfiguration: {BaseUrl: http://localhost:5000}
}AddHeadersToRequest和AuthenticationOptions这两个配置项在Ocelot的路由规则中起着至关重要的作用它们分别负责请求头的处理和认证授权的管理。 AddHeadersToRequest配置项允许你将上游请求客户端发送到Ocelot网关的请求的头部信息添加到转发到下游服务实际处理请求的API服务的请求中。在这个例子中Authorization: Authorization表示将上游请求中的Authorization头部原封不动地传递给下游服务。这通常用于传递认证信息例如JWT token。当下游服务需要知道用户的身份验证信息时这个配置就非常有用。通过传递Authorization头部下游服务可以根据token进行身份验证和授权。 AuthenticationOptions配置项则定义了Ocelot如何对上游请求进行认证和授权。AuthenticationProviderKey: Bearer指定了用于认证的方案是Bearer这通常与JWT认证一起使用。这意味着Ocelot会尝试使用Bearer认证方案来验证上游请求的身份。AllowedScopes: [api]定义了允许的OAuth 2.0 scopes。只有当上游请求携带的token包含api这个scope时Ocelot才会允许该请求通过这个路由。Scope是一种权限声明用于限制客户端可以访问的资源。通过配置AllowedScopes可以确保只有具有适当权限的客户端才能访问特定的API端点。如果请求的token不包含api这个scopeOcelot会拒绝该请求返回未经授权的错误。
3.3 业务服务开发
Weather项目使用的是带有示例的模板创建的因此就不用编写控制器了只用在控制器上加上[Authorize]即可。 打开Program类在该类中新增身份验证服务的代码需要注意的是这里的IssuerSigningKey需要和Gateway中的配置一样
builder.Services.AddAuthentication(Bearer).AddJwtBearer(Bearer, options {options.TokenValidationParameters new TokenValidationParameters{ValidateIssuerSigningKey true,IssuerSigningKey new SymmetricSecurityKey(Convert.FromBase64String(SguGSpvaRLMwnmnxiBHRdSxRpBDSiD88J1qp1czuD8)),TokenDecryptionKey new SymmetricSecurityKey(Convert.FromBase64String(na8LnVekSu5b3fgdUhyoKuLTMVGYLtgHrTTpKCB5VY)),ValidateIssuer false,ValidIssuer http://localhost:5002,ValidateAudience false};});这段代码和Gateway类似就不再讲解。看到这里大家一定很奇怪为什么网关服务已经配置了身份验证业务服务还要进行一次验证呢其实要分情况的如果项目是内网项目无需访问外网就可使用的话就不需要给业务负责增加身份验证的代码如果是外网项目就必须增加身份验证服务。
四、总结
本文详细介绍了微服务架构中如何通过JWT认证和IdentityServer实现统一的身份认证与授权以提升系统安全性。首先阐述了JWT的工作原理及其在Ocelot网关中的应用强调了JWT的无状态、轻量和跨平台兼容性并强调了密钥安全的重要性。其次介绍了IdentityServer作为.NET的开源身份认证与授权框架如何简化认证流程支持多种认证流程并与JWT结合实现安全令牌管理。最后通过一个集成Ocelot、IdentityServer和JWT认证的案例演示详细讲解了认证服务器、网关和业务服务的配置步骤包括OpenIddict的配置、数据库迁移、种子数据设置以及令牌端点的实现。该方案通过在网关层实现身份认证、权限校验和数据加密有效防范恶意攻击构建可靠的访问环境切实保护企业数据安全。