• ocelot+consul+identity4的使用demo


    Ocelot网关搭建

    • 搭建core控制台项目 本demo采用2.1版本 命名为APPIGateWay
    • 在Nuget包中添加Ocelot引用 选用8.0.0版本
    • 添加Ocelot.json 文件 内容为:
    {
      "ReRoutes": [
        {
          "DownstreamPathTemplate": "/api/{url}",//下游地址万能模板
          "DownstreamScheme": "http",
    //下游地址以及端口号 
    //配置多个是为了实现负载均衡功能 
    //注:最好使用IP 而不是localhost 
          "DownstreamHostAndPorts": [
            {
              "Host": "192.168.1.100",
              "Port": 21001
            },
            {
              "Host": "192.168.1.100",
              "Port": 21002
            }
          ],
          "UpstreamPathTemplate": "/api/{url}",//上游地址万能模板
          "UpstreamHttpMethod": [ "Get", "POST" ],//转发的请求类型
          "LoadBalancer": "LeastConnection",//负载均衡模式 
    //服务名称 需要和consul中配置的服务名称一致
          "ServiceName": "TestService",
          "UseServiceDiscovery": true,//是否启用服务发现
    //身份验证 所需属性 不验证可去除
          "AuthenticationOptions": {
            "AuthenticationProviderKey": "usergateway",//自定义key
            "AllowScopes": [ "TestService" ]//服务名称
          }
        },
    //下面为身份验证服务配置
        {
          "DownstreamPathTemplate": "/connect/token",
          "DownstreamScheme": "http",
          "DownstreamHostAndPorts": [
            {
              "Host": "192.168.1.100",
              "Port": 5100
            }
          ],
          "UpstreamPathTemplate": "/connect/token",
          "UpstreamHttpMethod": [ "Get", "POST" ],
          "LoadBalancer": "LeastConnection",
          "ServiceName": "IdentityService",
          "UseServiceDiscovery": true,
          "AuthenticationOptions": {
          }
        }
      ],
    //Ocelot全局配置
      "GlobalConfiguration": {
        "BaseUrl": "http://192.168.1.100:5000",//对外地址
    //服务发现地址配置
        "ServiceDiscoveryProvider": {
          "Host": "192.168.1.100",
          "Port": 8500
    
        }
    
      }
    }
    View Code
    • 修改Program.cs
    using Microsoft.AspNetCore;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using System;
    
    namespace APPIGateWay
    {
        class Program
        {
            static void Main(string[] args)
            {
                BuildWebHost(args).Run();
            }
            public static IWebHost BuildWebHost(string[] args) =>
                WebHost.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((hostingContext, builder) =>
                {
                    builder
                    .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
                    .AddJsonFile("Ocelot.json");//添加配置文件
                })
                    .UseStartup<Startup>()
                .UseUrls("http://192.168.1.100:5000")//使用指定url
                    .Build();
        }
    }
    View Code
    • 修改Startup.cs
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Ocelot.DependencyInjection;
    using Ocelot.Middleware;
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace APPIGateWay
    {
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                //services.AddMvc(); -- no need MVC
                // Ocelot
                services.AddOcelot(Configuration);
                //添加验证逻辑
                services.AddAuthentication()
                    .AddIdentityServerAuthentication("usergateway", options => {
                        options.Authority = "http://192.168.1.100:5100";
                        options.ApiName = "TestService";
                        options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Both;
                        options.ApiSecret = "test";
                        options.RequireHttpsMetadata = false;
                    });
                //增加日志
                //.AddOpenTracing(option =>
                //{
                //    //this is the url that the butterfly collector server is running on...
                //    option.CollectorUrl = "http://localhost:9618";
                //    option.Service = "Ocelot";
                //});
    
            }
    
            // 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.UseMvc(); -- no need MVC
                // Ocelot
                app.UseOcelot().Wait();
    
            }
        }
    }
    View Code

    至此 网关部分搭建完毕

    添加Consul注册帮助类

    • 创建core 类库项目
    • 添加以下代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.NetworkInformation;
    using System.Net.Sockets;
    
    namespace ConsulRegisterHelper
    {
        public class NetworkHelper
        {
            public static string LocalIPAddress
            {
                get
                {
                    UnicastIPAddressInformation mostSuitableIp = null;
                    var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
    
                    foreach (var network in networkInterfaces)
                    {
                        if (network.OperationalStatus != OperationalStatus.Up)
                            continue;
                        var properties = network.GetIPProperties();
                        if (properties.GatewayAddresses.Count == 0)
                            continue;
    
                        foreach (var address in properties.UnicastAddresses)
                        {
                            if (address.Address.AddressFamily != AddressFamily.InterNetwork)
                                continue;
                            if (IPAddress.IsLoopback(address.Address))
                                continue;
                            return address.Address.ToString();
                        }
                    }
    
                    return mostSuitableIp != null
                        ? mostSuitableIp.Address.ToString()
                        : "";
                }
            }
    
            public static int GetRandomAvaliablePort(int minPort = 1024, int maxPort = 65535)
            {
                Random rand = new Random();
                while (true)
                {
                    int port = rand.Next(minPort, maxPort);
                    if (!IsPortInUsed(port))
                    {
                        return port;
                    }
                }
            }
    
            private static bool IsPortInUsed(int port)
            {
                IPGlobalProperties ipGlobalProps = IPGlobalProperties.GetIPGlobalProperties();
                IPEndPoint[] ipsTCP = ipGlobalProps.GetActiveTcpListeners();
    
                if (ipsTCP.Any(p => p.Port == port))
                {
                    return true;
                }
    
                IPEndPoint[] ipsUDP = ipGlobalProps.GetActiveUdpListeners();
                if (ipsUDP.Any(p => p.Port == port))
                {
                    return true;
                }
    
                TcpConnectionInformation[] tcpConnInfos = ipGlobalProps.GetActiveTcpConnections();
                if (tcpConnInfos.Any(conn => conn.LocalEndPoint.Port == port))
                {
                    return true;
                }
    
                return false;
            }
        }
    }
    NetworkHelper.cs
    using Consul;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using System;
    
    namespace ConsulRegisterHelper
    {
        public static class AppBuilderExtensions
        {
            public static IApplicationBuilder RegisterConsul(this IApplicationBuilder app, IApplicationLifetime lifetime, ServiceEntity serviceEntity)
            {
                var consulClient = new ConsulClient(x => x.Address = new Uri($"http://{serviceEntity.ConsulIP}:{serviceEntity.ConsulPort}"));//请求注册的 Consul 地址
                var httpCheck = new AgentServiceCheck()
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册
                    Interval = TimeSpan.FromSeconds(3),//健康检查时间间隔,或者称为心跳间隔
                    HTTP = $"http://{serviceEntity.IP}:{serviceEntity.Port}{serviceEntity.HealthUrl}",//健康检查地址
                    Timeout = TimeSpan.FromSeconds(3)
                };
    
                // Register service with consul
                var registration = new AgentServiceRegistration()
                {
                    Checks = new[] { httpCheck },
                    ID = Guid.NewGuid().ToString(),
                    Name = serviceEntity.ServiceName,
                    Address = serviceEntity.IP,
                    Port = serviceEntity.Port,
                    Tags = new[] { $"urlprefix-/{serviceEntity.ServiceName}" }//添加 urlprefix-/servicename 格式的 tag 标签,以便 Fabio 识别
                };
    
                consulClient.Agent.ServiceRegister(registration).Wait();//服务启动时注册,内部实现其实就是使用 Consul API 进行注册(HttpClient发起)
                lifetime.ApplicationStopping.Register(() =>
                {
                    consulClient.Agent.ServiceDeregister(registration.ID).Wait();//服务停止时取消注册
                });
    
                return app;
            }
        }
    }
    Register.cs
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace ConsulRegisterHelper
    {
        public class ServiceEntity
        {
            public ServiceEntity()
            {
                HealthUrl = "/api/health";
            }
            /// <summary>
            /// 服务IP
            /// </summary>
            public string IP { get; set; }
            /// <summary>
            /// 服务端口号
            /// </summary>
            public int Port { get; set; }
            /// <summary>
            /// 服务名称
            /// </summary>
            public string ServiceName { get; set; }
            /// <summary>
            /// 服务发现地址
            /// </summary>
            public string ConsulIP { get; set; }
            /// <summary>
            /// 服务发现端口号
            /// </summary>
            public int ConsulPort { get; set; }
            /// <summary>
            /// 健康检查地址默认为/api/health
            /// </summary>
            public string HealthUrl { get; set; }
        }
    }
    ServiceEntity.cs

    至此帮助类搭建完毕 该帮助类主要是为了方便服务注册使用

    身份验证服务搭建

    • 新建空web core项目 命名IdentityService
    • 下载Quickstart 发布版放入 
    • 添加配置文件 appsettings.json
    {
      "Service": {
        "Name": "IdentityService",
        "Port": "5100"
      },
      "Consul": {
        "IP": "localhost",
        "Port": "8500"
      },
      "Logging": {
        "IncludeScopes": false,
        "Debug": {
          "LogLevel": {
            "Default": "Warning"
          }
        },
        "Console": {
          "LogLevel": {
            "Default": "Warning"
          }
        }
      }
    }
    appsettings.json
    • 添加健康检查控制器
    • 添加以下文件
    using IdentityServer4.Models;
    using IdentityServer4.Test;
    using Microsoft.Extensions.Configuration;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace IdentityService
    {
    //测试使用内容
        public class InMemoryConfiguration
        {
            public static IConfiguration Configuration { get; set; }
            /// <summary>
            /// Define which APIs will use this IdentityServer
            /// </summary>
            /// <returns></returns>
            public static IEnumerable<ApiResource> GetApiResources()
            {
                return new[]
                {
                    new ApiResource("TestService", "测试服务1"),
                    new ApiResource("TestService2", "测试服务2")
                };
            }
    
            /// <summary>
            /// Define which Apps will use thie IdentityServer
            /// </summary>
            /// <returns></returns>
            public static IEnumerable<Client> GetClients()
            {
                return new[]
                {
                    new Client
                    {
                        ClientId = "c1",
                        ClientSecrets = new [] { new Secret("secret1".Sha256()) },
                        AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
                        AllowedScopes = new [] { "TestService", "TestService2" }
                    },
                    new Client
                    {
                        ClientId = "c-low",
                        ClientSecrets = new [] { new Secret("clowsecret".Sha256()) },
                        AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
                        AllowedScopes = new [] { "TestService" }
                    }
                };
            }
        }
    }
    InMemoryConfiguration.cs
    using IdentityServer4.Models;
    using IdentityServer4.Services;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace IdentityService
    {
        public class ProfileService : IProfileService
        {
            public async Task GetProfileDataAsync(ProfileDataRequestContext context)
            {
                var claims = context.Subject.Claims.ToList();
                context.IssuedClaims = claims.ToList();
            }
    
            public async Task IsActiveAsync(IsActiveContext context)
            {
                context.IsActive = true;
            }
        }
    }
    ProfileService.cs
    using IdentityServer4.Models;
    using IdentityServer4.Validation;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Claims;
    using System.Threading.Tasks;
    
    namespace IdentityService
    {
        public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
        {
            public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
            {
                //ToDo:验证自定义用户
                //LoginUser loginUser = null;
                bool isAuthenticated = context.UserName=="aaa"&&context.Password=="1"? true :false; //loginUserService.Authenticate(context.UserName, context.Password, out loginUser);
                if (!isAuthenticated)
                {
                    context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "账户名密码错误");
                }
                else
                {
                    context.Result = new GrantValidationResult(
                        subject: context.UserName,
                        authenticationMethod: "custom",
                        claims: new Claim[] {
                            new Claim("Name", context.UserName),
                            new Claim("Id", ""),
                            new Claim("RealName", ""),
                            new Claim("Email", "")
                        }
                    );
                }
                return Task.CompletedTask;
            }
        }
    }
    ResourceOwnerPasswordValidator.cs
    • 修改以下文件
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Logging;
    
    namespace IdentityService
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                BuildWebHost(args).Run();
            }
    
            public static IWebHost BuildWebHost(string[] args) =>
                WebHost.CreateDefaultBuilder(args)
                    .UseStartup<Startup>()
                 .UseUrls("http://192.168.1.100:5100")
                    .Build();
        }
    }
    Program.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using ConsulRegisterHelper;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    using Microsoft.Extensions.PlatformAbstractions;
    
    namespace IdentityService
    {
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
               
    
                services.AddMvc();
                InMemoryConfiguration.Configuration = this.Configuration;
                services.AddIdentityServer()
               .AddDeveloperSigningCredential()//开发临时证书
                .AddInMemoryClients(InMemoryConfiguration.GetClients())
                .AddInMemoryApiResources(InMemoryConfiguration.GetApiResources())
                .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()//添加自定义验证
                .AddProfileService<ProfileService>();
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
                // authentication
                app.UseMvc();
                app.UseIdentityServer();
                //启用UI
                app.UseStaticFiles();
                app.UseMvcWithDefaultRoute();
                // register this service
                app.RegisterConsul(lifetime, new ServiceEntity
                {
                    IP = NetworkHelper.LocalIPAddress,
                    Port = Convert.ToInt32(Configuration["Service:Port"]),
                    ServiceName = Configuration["Service:Name"],
                    ConsulIP = Configuration["Consul:IP"],
                    ConsulPort = Convert.ToInt32(Configuration["Consul:Port"])
                });
            }
        }
    }
    Startup.cs

    身份验证服务搭建完毕

    测试服务搭建

    • 创建TestService1 模板为coreAPi
    • 引用之前创建的ConsulRegisterHelper
    • 添加HealthController.cs 
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    
    namespace TestService1.Controllers
    {
        [Route("api/[controller]")]
        public class HealthController : Controller
        {
            // GET api/values
            [HttpGet]
            public string Get()
            {
                return "ok";
            }
        }
    }
    View Code
    • 修改Startup.cs
      public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseMvc();
                // register this service
                app.RegisterConsul(lifetime, new ServiceEntity
                {
                    IP = NetworkHelper.LocalIPAddress,
                    Port = Convert.ToInt32(Configuration["Service:Port"]),
                    ServiceName = Configuration["Service:Name"],
                    ConsulIP = Configuration["Consul:IP"],
                    ConsulPort = Convert.ToInt32(Configuration["Consul:Port"])
                });
            }
    View Code

    搭建完毕

    其他服务类似于此服务

    搭建完毕后可用http://localhost:5000/connect/token 访问获取token 

    需要传入 这些参数。

    获取到token后 可访问 http://localhost:5000/api/values 获取方法内容

    需要在headers中加入 Authorization 并附上定义的前缀+空格+token 即可请求到数据 

    附上结果图

    参考博客:

    微服务系列博客

    https://www.cnblogs.com/edisonchou/p/dotnetcore_microservice_foundation_blogs_index.html

    token权限控制

    https://www.cnblogs.com/jaycewu/p/7791102.html

    Ocelot+identity

    http://www.cnblogs.com/liyouming/p/9025084.html

  • 相关阅读:
    javascript对象继承的实现
    浏览器兼容问题汇总<转>
    DOM笔记
    Ajax日记
    学习态度
    项目1
    导航项目-整体布局
    有关布局
    导航项目开始
    windows 服务 定时程序 跑不出数据
  • 原文地址:https://www.cnblogs.com/nontracey/p/9294692.html
Copyright © 2020-2023  润新知