本文结构
是否需要将IdentityServer置于Ocelot API网关之后,由Ocelot API网关对其进行保护,需要看具体的架构需求。在微软官方的eShopOnContainers架构案例中,由于根据不同的业务需求,设置了多个Ocelot API网关,因此,这个案例并没有将IdentityServer置于Ocelot API网关之后。也就是说,它所采用的架构大致如下:
这种架构需要在实际部署的时候,将IdentityServer的前端页面资源的地址直接暴露出来,以便前端应用程序能够直接跳转到IdentityServer的登录界面完成登录。实现这种架构其实非常容易,无需对IdentityServer和Ocelot API网关进行任何特殊配置,Ocelot只需要负责基于业务的微服务即可。
在更多的情况下,我还是倾向于将IdentityServer也置于Ocelot API网关之后,让Ocelot保护起来,以便外界访问服务端能够有一个统一的接口,在部署上也显得更加方便,此时,架构将变成下面的形式:
通过默认的IdentityServer和Ocelot的配置,无法达到这样的架构拓扑,本文就按照操作步骤,介绍一下如何实现将IdentityServer置于Ocelot API网关之后。
Step 1:在Ocelot的配置中代理IdentityServer路由
第一步很容易理解,就是将IdentityServer也看成是一个微服务,然后在Ocelot中修改配置,使得IdentityServer能够由Ocelot所代理。首先修改ocelot.configuration.json文件,加入针对IdentityServer的配置:
{ "DownstreamPathTemplate": "/auth/{everything}", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 9001 } ], "UpstreamPathTemplate": "/auth/{everything}", "UpstreamHttpMethod": [ "Get", "Post", "Options" ], "UpstreamHeaderTransform": { "X-Forwarded-Host": "{UpstreamHost}" }, "SwaggerKey": "AuthService" }
并在Ocelot项目中,将Authority改为/auth路径:
var idsAuthority = "https://localhost:9000/auth";
关注两点:
- 下游的IdentityServer相对路径已经为/auth,而不是根路径/,因此,接下来需要修改IdentityServer的路由,将所有的资源路由都改到/auth下
- 此处使用了UpstreamHeaderTransform,将当前访问的主机地址传递到下游IdentityServer,让IdentityServer的Base URL变为当前访问的地址
经过上面的两个设置,原本IdentityServer的API,例如:https://localhost:9001/connect/token,就变成了Ocelot的地址,如:https://localhost:9000/auth/connect/token。
Step 2:修改IdentityServer路由
在IdentityServer的项目中,打开HostingExtensions.cs文件,修改ConfigurePipeline方法:
public static WebApplication ConfigurePipeline(this WebApplication app) { app.UseSerilogRequestLogging(); if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.Map("/auth", app => { app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); endpoints.MapRazorPages().RequireAuthorization(); }); app.UseIdentityServer(); }); return app; }
此时,所有的IdentityServer的资源都将在/auth路由下开放,如果现在运行IdentityServer项目,打开的浏览器中就会显示404 Not Found,原因是IdentityServer的主页已经不再是在根路径下了,而是在/auth下。为了便于调试,可以考虑在launchSettings.json中,修改launchUrl设置:
{ "profiles": { "SelfHost": { "commandName": "Project", "launchUrl": "auth", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:9001" } } }
Step 3:处理X-Forwarded-Host Http请求Header
在IdentityServer项目中,打开HostingExtensions.cs文件,在ConfigureServices方法中加入:
builder.Services.Configure<ForwardedHeadersOptions>(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedHost; options.ForwardLimit = 2; //Limit number of proxy hops trusted options.KnownNetworks.Clear(); options.KnownProxies.Clear(); });
并且在ConfigurePipeline方法中的最上方,加入:
app.UseForwardedHeaders();
到此为止,相关修改已经完成,可以测试一下。
在Blazor WebAssembly应用程序中测试
如何在Blazor WebAssembly中集成IdentityServer实现身份验证,我会在后续的文章中介绍。
在一个集成了IdentityServer认证系统的Blazor WebAssembly应用程序中,对上面的更改进行测试。我们的Blazor WebAssembly App是部署在9200端口,IdentityServer是部署在9001端口,而Ocelot则是部署在9000端口。直接启动Blazor WebAssembly应用程序,点击Login按钮:
然后,页面会跳转到IdentityServer的登录界面,注意URL地址已经是Ocelot API网关的地址:
输入用户名、密码,然后点击Login按钮进行登录,登录成功后,会返回我们的Blazor WebAssembly应用主界面: