修改完后,重新通过调用这个register-account API来新建一个用户来进行测试,一切正常的话,就可以通过Ocelot API网关中的RouteClaimsRequirement来完成授权了。
通过Ocelot网关授权的另一种做法是使用代码实现。通过代码方式,可以实现更为复杂的授权策略,我们仍然以“角色”作为授权参照,我们可以首先定义所需的授权策略:
public void ConfigureServices(IServiceCollection services) { services.AddOcelot(); services.AddAuthentication() .AddIdentityServerAuthentication("AuthKey", options => { options.Authority = "http://localhost:7889"; options.RequireHttpsMetadata = false; }); services.AddAuthorization(options => { options.AddPolicy("admin", builder => builder.RequireRole("admin")); options.AddPolicy("superadmin", builder => builder.RequireRole("superadmin")); }); services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader())); }然后使用Ocelot的AuthorisationMiddleware中间件,来定义我们的授权处理逻辑:
app.UseOcelot((b, c) => { c.AuthorisationMiddleware = async (ctx, next) => { if (ctx.DownstreamReRoute.DownstreamPathTemplate.Value == "/weatherforecast") { var authorizationService = ctx.HttpContext.RequestServices.GetService<IAuthorizationService>(); var result = await authorizationService.AuthorizeAsync(ctx.HttpContext.User, "superadmin"); if (result.Succeeded) { await next.Invoke(); } else { ctx.Errors.Add(new UnauthorisedError($"Fail to authorize policy: admin")); } } else { await next.Invoke(); } }; b.BuildCustomOcelotPipeline(c).Build(); }).Wait();当然,上面的BuildCustomOcelotPipeline方法的目的就是将一些默认的Ocelot中间件加入到管道中,否则整个Ocelot框架是不起作用的。我将这个方法定义为一个扩展方法,代码如下:
public static class Extensions { private static void UseIfNotNull(this IOcelotPipelineBuilder builder, Func<DownstreamContext, Func<Task>, Task> middleware) { if (middleware != null) { builder.Use(middleware); } } public static IOcelotPipelineBuilder BuildCustomOcelotPipeline(this IOcelotPipelineBuilder builder, OcelotPipelineConfiguration pipelineConfiguration) { builder.UseExceptionHandlerMiddleware(); builder.MapWhen(context => context.HttpContext.WebSockets.IsWebSocketRequest, app => { app.UseDownstreamRouteFinderMiddleware(); app.UseDownstreamRequestInitialiser(); app.UseLoadBalancingMiddleware(); app.UseDownstreamUrlCreatorMiddleware(); app.UseWebSocketsProxyMiddleware(); }); builder.UseIfNotNull(pipelineConfiguration.PreErrorResponderMiddleware); builder.UseResponderMiddleware(); builder.UseDownstreamRouteFinderMiddleware(); builder.UseSecurityMiddleware(); if (pipelineConfiguration.MapWhenOcelotPipeline != null) { foreach (var pipeline in pipelineConfiguration.MapWhenOcelotPipeline) { builder.MapWhen(pipeline); } } builder.UseHttpHeadersTransformationMiddleware(); builder.UseDownstreamRequestInitialiser(); builder.UseRateLimiting(); builder.UseRequestIdMiddleware(); builder.UseIfNotNull(pipelineConfiguration.PreAuthenticationMiddleware); if (pipelineConfiguration.AuthenticationMiddleware == null) { builder.UseAuthenticationMiddleware(); } else { builder.Use(pipelineConfiguration.AuthenticationMiddleware); } builder.UseClaimsToClaimsMiddleware(); builder.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware); if (pipelineConfiguration.AuthorisationMiddleware == null) { builder.UseAuthorisationMiddleware(); } else { builder.Use(pipelineConfiguration.AuthorisationMiddleware); } builder.UseClaimsToHeadersMiddleware(); builder.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware); builder.UseClaimsToQueryStringMiddleware(); builder.UseLoadBalancingMiddleware(); builder.UseDownstreamUrlCreatorMiddleware(); builder.UseOutputCacheMiddleware(); builder.UseHttpRequesterMiddleware(); return builder; } }与上文所提交的“后端服务授权”类似,我们需要在Ocelot API网关上定义并实现授权策略,有可能是需要设计一些框架来简化用户数据的访问并提供灵活的、可复用的授权逻辑,由于这部分内容跟每个应用程序的业务关系较为密切,所以本文也就不深入讨论了。
总结至此,有关Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权的介绍,就告一段落了。通过四篇文章,我们从零开始,一步步搭建微服务、基于IdentityServer4的IdentityService、Ocelot API网关以及Angular单页面应用,并逐步介绍了认证与授权的实现过程。虽然没有最终实现一个可被重用的授权框架,但基本架构也算是完整了,今后有机会我可以再补充认证、授权的相关内容,欢迎阅读并提宝贵意见。
源代码访问以下Github地址以获取源代码:
https://github.com/daxnet/identity-demo