zoukankan      html  css  js  c++  java
  • AspNetCore网关集成Swagger访问使用IdentityServer保护的webapi项目

    创建webapi项目

      创建四个webapi项目,两个处理业务,一个网关,一个验证中心。四个项目对应的端口如下,

    ApiGateway:1999

    IdentityServer:16690

    Services.Api1:2108

    Services.Api2:2343

    添加Swagger支持

      在两个业务项目中分别引用Swashbuckle.AspNetCore,目前是最新版本是4.0.1。在项目属性面板,设置输出xml文档,swagger可以读取xml注释生成json文件,在swagger ui页面中显示。但是选择输出xml文档后,对于没有xml注释的类和方法会显示警告,可以在项目属性面板中【错误和警告】选项,取消显示警告中添加1591,这样就可以不显示缺少xml注释的警告了。对于强迫症的你暂时可以平复一下了,当然,真的强迫症的话,肯定会全部加上xml注释的。(¬_¬)瞄

       Startup.ConfigureServices

    public void ConfigureServices(IServiceCollection services)
            {
               services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    
                services.AddSwaggerGen(options =>
                {
                    //SwaggerDoc的第一个参数要与Configure中SwaggerEndPoint中的版本名称一致
                    //既可以使用版本号,也可以使用自定义名称
                    options.SwaggerDoc("ServiceApiTwo", new Info
                    {
                        Title = "Services.Api #two",
                        Version = "v1",
                        Description = "服务api #two",
                        License = new License
                        {
                            Name = "APL2.0",
                            Url = "https://opensource.org/licenses/Apache-2.0"
                        },
                        Contact = new Contact
                        {
                            Name = "原来是李",
                            Url = "https://www.cnblogs.com/falltakeman"
                        }
                    });
                    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                    var xmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, xmlFile);
                    options.IncludeXmlComments(xmlPath);
                    
                });
            }

      Startup.Configure

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
    
                    app.UseSwagger(c=>
                    {
                        c.RouteTemplate = "{documentName}/swagger.json";
                    });
                    app.UseSwaggerUI(u =>
                    {
                        u.SwaggerEndpoint("/ServiceApiTwo/swagger.json", "ServiceApiTwo");
                        u.DocumentTitle = "Service Api #2 文档";
                    });
                }
                
                app.UseMvc();
            }

       配置好启动项目看看。

      通过swagger发起请求,响应正常。

     

    配置网关项目

      使用目前比较流行的开源网关服务Ocelot,APIGateway项目添加引用Ocelot、Ocelot.Provider.Consul、Ocelot.Provider.Polly、Ocelot.Cache.CacheManager,目前用到的版本是13.5.2。要在网关中统一查看各个服务的swagger文档,还需引用Swash.AspNetCore。Consul的配置先不说了。

      在项目根路径下添加OcelotConfig.json配置文件。处理正常的路由转发外,要在网关swagger页面访问业务api,还需要配置swagger路由转发。

    {
      "GlobalConfiguration": {
        //外部访问路径
        "BaseUrl": "http://localhost:1999",
        //限速配置
        "RateLimitOptions": {
          //白名单
          "ClientWhitelist": [],
          "EnableRateLimiting": true,
          //限制时间段,例如1s,5m,1h,1d
          "Period": "1s",
          //等待重试等待的时间间隔(秒)
          "PeriodTimespan": 1,
          //限制
          "Limit": 1,
          //自定义消息
          "QuotaExceededMessage": "单位时间内请求次数超过限制。",
          "HttpStatusCode": 999
        },
        //服务发现配置
        "ServiceDiscoveryProvider": {
          "Host": "localhost",
          "Port": 8500,
          "Type": "PollConsul",
          "PollingInterval": 1000
        },
        //熔断配置
        "QoSOptions": {
          "ExceptionsAllowedBeforeBreaking": 3,
          "DurationOfBreak": 5,
          //超时值(毫秒)
          "TimeoutValue": 5000
        }
      },
      "ReRoutes": [
        // Api#one项目配置
        {
          "UpstreamPathTemplate": "/gateway/one/{url}", //上游路径模板
          "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ], //上游HTTP请求方法
          "DownstreamPathTemplate": "/api/{url}", //下游路径模板
          "DownstreamScheme": "http", //下游协议 https/http
          "ServiceName": "ServiceApiOne", //服务名称(结合服务发现使用)
          "UseServiceDiscovery": true, //启用服务发现
          "LoadBalancer": "RoundRobin", //负载均衡算法:RoundRobin-轮询;LeastConnection-最少连接数(最空闲的服务器);NoLoadBalancer-总是发送往第一个请求或者服务发现
          //下游主机与端口,允许配置多个
          "DownstreamHostAndPorts": [
            //{
            //  "Host": "ip",
            //  "Port": 80
            //},
            {
              "Host": "localhost",
              "Port": 2108
            }
          ],
          //熔断配置,在请求下游服务时使用断路
          "QoSOptions": {
            "ExceptionsAllowedBeforeBreaking": 3,
            "DurationOfBreak": 10,
            "TimeoutValue": 5000
          },
          //权限配置
          //"AuthenticationOptions": {
          //  "AuthenticationProviderKey": "Bearer",
          //  "AllowedScopes": []
          //}
        },
        // Api#two项目配置
        {
          "UpstreamPathTemplate": "/gateway/two/{url}",
          "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ],
          "DownstreamPathTemplate": "/api/{url}",
          "DownstreamScheme": "http",
          "ServiceName": "ServiceApiTwo",
          "UseServiceDiscovery": true,
          "LoadBalancer": "RoundRobin",
          "DownstreamHostAndPorts": [
            //{
            //  "Host": "ip",
            //  "Port": 80
            //},
            {
              "Host": "localhost",
              "Port": 2343
            }
          ],
          "QoSOptions": {
            "ExceptionsAllowedBeforeBreaking": 3,
            "DurationOfBreak": 10,
            "TimeoutValue": 5000
          },
          //"AuthenticationOptions": {
          //  "AuthenticationProviderKey": "Bearer",
          //  "AllowedScopes": []
          //}
        },
        //swagger api2配置
        {
          "UpstreamPathTemplate": "/ServiceApiTwo/swagger.json",
          "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ],
          "DownstreamPathTemplate": "/ServiceApiTwo/swagger.json",
          "DownstreamScheme": "http",
          "DownstreamHostAndPorts": [
            {
              "Host": "localhost",
              "Port": 2343
            }
          ]
        },
        //swagger api1多版本配置v1.0
        {
          "UpstreamPathTemplate": "/ServiceApiOne/1.0/swagger.json",
          "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ],
          "DownstreamPathTemplate": "/ServiceApiOne/1.0/swagger.json",
          "DownstreamScheme": "http",
          "DownstreamHostAndPorts": [
            {
              "Host": "localhost",
              "Port": 2108
            }
          ]
        },
        //swagger api1多版本配置v2.0
        {
          "UpstreamPathTemplate": "/ServiceApiOne/2.0/swagger.json",
          "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ],
          "DownstreamPathTemplate": "/ServiceApiOne/2.0/swagger.json",
          "DownstreamScheme": "http",
          "DownstreamHostAndPorts": [
            {
              "Host": "localhost",
              "Port": 2108
            }
          ]
        }
      ]
    }

      Startup.ConfigureServices注册swagger和Ocelot网关服务,ConfigureServices中的swagger配置和业务api中一样,

    services.AddOcelot(Configuration)
                    .AddConsul()
                    .AddCacheManager(c => c.WithDictionaryHandle())
                    .AddPolly();
    
    services.AddSwaggerGen(options =>
                {
                    options.SwaggerDoc(Configuration["Swagger:Name"], new Info
                    {
                        Title = Configuration["Swagger:Title"],
                        Version = Configuration["Swagger:Version"],
                        Description = Configuration["Swagger:Description"],
                        License = new License
                        {
                            Name = Configuration["Swagger:License:Name"],
                            Url = Configuration["Swagger:License:Url"]
                        },
                        Contact = new Contact
                        {
                            Name = Configuration["Swagger:Contact:Name"],
                            Email = Configuration["Swagger:Contact:Email"],
                            Url = Configuration["Swagger:Contact:Url"]
                        }
                    });
                });

      Startup.Configure中,我是使用了配置文件,将业务api的swagger节点写在了配置文件中。

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                    // 配置文件中的SwaggerName为业务api中是SwaggerEndPoint名称,有版本号的带上版本号
                    var apis = Configuration["SwaggerApis:SwaggerName"].Split(';').ToList();
                    app.UseSwagger();
                    app.UseSwaggerUI(options=>
                    {
                        //显示注册到网关的api接口
                        apis.ForEach(key =>
                        {
                            options.SwaggerEndpoint($"/{key}/swagger.json", key);
                        });
                        options.DocumentTitle = "api网关";
                    });
    
                }
                else
                {
                    app.UseExceptionHandler("/Home/Error");
                }
    
                app.UseStaticFiles();
                app.UseCookiePolicy();
    
                app.UseMvc(routes =>
                {
                    routes.MapRoute(
                        name: "default",
                        template: "{controller=Home}/{action=Index}/{id?}");
                });
    
                app.UseOcelot().Wait(); // 使用Ocelot网关中间件
    
            }

      修改ApiGateway项目Program.cs将配置文件添加进来。

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
                WebHost.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((hostingContext,config)=>
                {
                    var env = hostingContext.HostingEnvironment;
                    //根据环境变量加载不同的json配置
                    config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
                    .AddJsonFile("OcelotConfig.json")//网关配置
                    .AddEnvironmentVariables();//环境变量
                })
                .ConfigureLogging((hostingContext,logging)=>
                {
                    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                    
                    logging.AddConsole();
                    //添加调试日志
                    logging.AddDebug();
                })
                .UseStartup<Startup>();

      网关配置了,swagger也配置了,启动业务api和网关服务看看效果。

       两个业务api的swagger文档都可以正常查看。发请求看一下,结果响应404,仔细看一下,请求的服务地址是网关服务的地址,而不是业务api的地址,难道是Ocelot网关路由配置错了?使用Postman发一个GET请求看看,localhost:1999/gateway/two/values,网关转发到localhost:2343/api/values,响应正常。

       看swashbuckle文档这一段,将业务api中Configure加上这一段后再次通过网关发起请求,结果出现TypeError。既然出错了,打开浏览器调试工具看一下就明白了,Failed to load http://localhost:2343/api/ApiTwo: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:1999' is therefore not allowed access.

      网关请求业务api跨域了,要让业务api允许来自网关的请求,需要设置业务api跨域请求政策。加上下面的配置后,网关请求正常了。

      修改Startup.Configure

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
    
                    app.UseSwagger(c=>
                    {
                        //处理网关通过swagger访问
                        c.PreSerializeFilters.Add((swaggerDoc, httpReq) => swaggerDoc.Host = httpReq.Host.Value);
                        c.RouteTemplate = "{documentName}/swagger.json";
                    });
                    app.UseSwaggerUI(u =>
                    {
                        u.SwaggerEndpoint("/ServiceApiTwo/swagger.json", "ServiceApiTwo");
                        u.DocumentTitle = "Service Api #2 文档";
                    });
                }
    
                // 允许网关访问
                app.UseCors(options =>
                {
                    options.WithOrigins("http://localhost:1999")
                    .AllowAnyHeader()
                    .AllowAnyMethod();
                });
                app.UseMvc();
            }

     

     使用IdentityServer4保护webapi

       先前已经创建IdentityServer项目,添加引用IdentityServer4.AspNetIdentity(2.5.0)、IdentityServer4.EntityFramework(2.5.0)。新建一个类IdentityServerConfig,里面定义四个方法GetApiResources、GetIdentityResources、GetClients、GetTestUsers,具体代码就不贴了,看一下Startup。生产环境的话,当然要用数据库,这里不讨论IdentityServer4的使用。

    public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    
                services.AddIdentityServer()
                    .AddDeveloperSigningCredential()
                    .AddInMemoryPersistedGrants()
                    .AddTestUsers(IdentityServerConfig.GetTestUsers())
                    .AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResources())
                    .AddInMemoryApiResources(IdentityServerConfig.GetApiResources())
                    .AddInMemoryClients(IdentityServerConfig.GetClients(Configuration));
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseIdentityServer();
    
                app.UseMvc();
            }

      将网关和两个api项目对应的ApiResources和Clients分别为api-gateway、service-api-one、service-api-two,两个api客户端AllowedScope为自己,网关的AllowedScope为自己和两个api客户端。在需要保护的三个项目中添加引用IdentityServer4.AccessTokenValidation(2.7.0),修改Startup的ConfigureServices,添加如下代码。

    //使用IdentityServer4
                services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
                    .AddIdentityServerAuthentication(options =>
                    {
                        options.ApiName = "service-api-two";
                        options.Authority = "http://localhost:16690"; // IdentityServer验证服务
                        options.RequireHttpsMetadata = false;
                        options.EnableCaching = true;
                    });

      Startup.Configure中添加app.UseAuthentication();

      要在swagger中访问需要验证的api,需要在swagger配置中添加安全验证。

    services.AddSwaggerGen(options =>
                {
                    //SwaggerDoc的第一个参数要与Configure中SwaggerEndPoint中的版本名称一致
                    //既可以使用版本号,也可以使用自定义名称
                    options.SwaggerDoc("ServiceApiTwo", new Info
                    {
                        Title = "Services.Api #two",
                        Version = "v1",
                        Description = "服务api #two",
                        License = new License
                        {
                            Name = "MIT",
                            Url = "https://mit-license.org/"
                        },
                        Contact = new Contact
                        {
                            Name = "原来是李",
                            Url = "http://www.cnblogs.com/falltakeman"
                        }
                    });
                    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                    var xmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, xmlFile);
                    options.IncludeXmlComments(xmlPath);
                    // swagger访问需要验证的api
                    options.AddSecurityDefinition("Bearer", new ApiKeyScheme
                    {
                        In = "header",
                        Name = "Authorization",
                        Type = "apiKey",
                        Description = "Bearer {token}"
                    });
                    options.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
                    {
                        {
                            "Bearer",
                            Enumerable.Empty<string>()
                        }
                    });
                });

      在api控制器中,在需要保护的api上添加[Authorize]特性,没有授权的情况下访问受限api会报401错误。

       使用postman获取token,在swagger中填写token,再次发起请求,响应正常。

       在ApiGateway的Startup.ConfigureServices添加Authentication,在Services.AddSwaggerGen添加相应代码,启动项目在app.UseOcelot().Wait()抛出异常:Scheme already exists: BearerIdentityServerAuthenticationJwt. 最终使用了下面的方式。在ApiGateway项目中通过swagger也可以访问业务api了。

    Action<IdentityServerAuthenticationOptions> isaOpt = option =>
                {
                    option.Authority = Configuration["IdentityService:Uri"];
                    option.RequireHttpsMetadata = Convert.ToBoolean(Configuration["IdentityService:UseHttps"]);
                    option.ApiName = Configuration["IdentityService:ApiName"];
                    option.ApiSecret = Configuration["IdentityService:ApiSecret"];
                    option.SupportedTokens = SupportedTokens.Both;
                };
                services.AddAuthentication().AddIdentityServerAuthentication(Configuration["IdentityService:DefaultScheme"], isaOpt);

      但是配置中的IdentityService:DefaultScheme不可以是"Bearer",试验配置的是"IdentityBearer",不知为何不可以是"Bearer",不知道有没有懂这个的可以指点一二。

    the end...

  • 相关阅读:
    tolua总结(二)
    convirt管理端离线安装
    c++运用backtrace追踪函数调用的堆栈
    不知道自己适不适合作程序员
    这次六级又没过
    我也有blog了,呵呵!
    代理 XP”组件已作为此服务器安全配置的一部分被关闭。系统管理员可以使用 sp_configure 来启用“代理 XP”。
    SQLServer中的GROUPING,ROLLUP和CUBE
    android studio ndk 环境配置成eclipse模式
    寄存器小记
  • 原文地址:https://www.cnblogs.com/fallTakeMan/p/11289713.html
Copyright © 2011-2022 走看看