• asp.net core网关Ocelot的简单介绍& Ocelot集成Identity认证


    文章简介

    •  Ocelot网关简介

    •  Ocelot集成Idnetity认证处理

    Ocelot网关简介

    Ocelot是一个基于netcore实现的API网关,本质是一组按特定顺序排列的中间件。Ocelot内部实现了路由转发,限流,熔断,请求聚合,服务发现(集成consul,eureka等),负载均衡,认证(集成Identity)功能。

    这里简单介绍下ocelot的配置文件,也就是说以下图为例,请求地址为localhost:18002/users会被转发到localhost:18000/api/users

    更多关于Ocelot的介绍可以看https://www.cnblogs.com/jesse2013/p/net-core-apigateway-ocelot-docs.html这篇博客或者https://ocelot.readthedocs.io/en/latest/index.html官方文档。

    Ocelot集成Identity认证

      这里我们实现一个Ocelot集成Idnetity做认证的Demo;我们这里客户端请求ocelot网关服务,ocelot网关服务集成Idnetity获取token,再通过返回的token请求用户信息服务,如下图所示。这里扩展一个知识点,我们的Identity服务使用扩展认证,这个认证需要实现IExtensionGrantValidator接口的ValidateAsync方法,从http请求上下文中获取自定义参数,获取方法为context.Request.Raw。(oauth2默认的认证方式有password,authcode等,扩展认证文档=》http://docs.identityserver.io/en/latest/topics/extension_grants.html?highlight=IExtensionGrantValidator

     

     首先我们创建三个服务,分别为Ocelot网关服务(端口号设置为18002),Identity认证服务(端口号设置为18001),UserInfo用户信息服务(端口号设置为18000),如下图所示=》

     

    • 首先我们配置User.API服务,这个服务很简单,开放一个返回用户信息的端口,关键代码如下所示 =》 
     1 namespace User.API.Controllers
     2 {
     3     [Route("api/users")]
     4     public class UserController : BaseController
     5     {
     6         private readonly UserContext _userContext;
     7         private ILogger<UserController> _logger;
     8         public UserController(UserContext userContext, ILogger<UserController> logger)
     9         {
    10             _userContext = userContext;
    11             _logger = logger;
    12         }
    13         [HttpGet]
    14         public async Task<IActionResult> Get()
    15 
    16         {
    17             var user = await _userContext.Set<AppUser>()
    18                 .AsNoTracking()
    19                 .Include(u => u.userProperties)
    20                 .FirstOrDefaultAsync(t => t.Id == 1);
    21             if (user == null)
    22             {
    23                 _logger.LogError("登录用户为空");
    24                 throw new UserOperationException("用户登录异常");
    25             }
    26             return Json(user);
    27         }
    28 ..... other
    User.Api
     1         [Route("check_or_create")]
     2         [HttpPost]
     3         public async Task<IActionResult> CheckOrCreate(string phone)
     4         {
     5             var user = await _userContext.Users.SingleOrDefaultAsync(u => u.Phone == phone);
     6 
     7             if (user == null)
     8             {
     9                 user = new AppUser { Phone = phone };
    10                 _userContext.Users.Add(new AppUser { Phone = phone });
    11                 await _userContext.SaveChangesAsync();
    12             }
    13             return Ok(new {
    14                 user.Id,
    15                 user.Name,
    16                 user.Company,
    17                 user.Title,
    18                 user.Avatar
    19             });
    20         }
    User.Api 验证用户手机号,返回用户信息
    • 然后配置我们的Identity认证服务,引入IdnttiyServer4 nuget包,添加Ids4配置文件Config.cs。注意:这里的client_grant_type为sms_auth_code
     1     public class Config
     2     {
     3         public static IEnumerable<Client> GetClients()
     4         {
     5             return new List<Client>{
     6                 new Client{
     7                     ClientId = "android",
     8                     ClientSecrets = new List<Secret>
     9                     {
    10                         new Secret("secret".Sha256())
    11                     },
    12                     RefreshTokenExpiration  = TokenExpiration.Sliding,
    13                     AllowOfflineAccess = true,
    14                     RequireClientSecret = false,
    15                     AllowedGrantTypes = new List<string>{"sms_auth_code"},
    16                     AlwaysIncludeUserClaimsInIdToken = true,
    17                     AllowedScopes = new List<string>
    18                     {
    19                         "gateway_api",
    20                         IdentityServerConstants.StandardScopes.OfflineAccess,
    21                         IdentityServerConstants.StandardScopes.OpenId,
    22                         IdentityServerConstants.StandardScopes.Profile
    23                     }
    24                 }
    25             };
    26         }
    27         public static IEnumerable<IdentityResource> GetIdentityResources()
    28         {
    29             return new List<IdentityResource>
    30             {
    31                 new IdentityResources.OpenId(),
    32                 new IdentityResources.Profile()
    33             };
    34         }
    35         public static IEnumerable<ApiResource> GetApiResources()
    36         {
    37             return new List<ApiResource>
    38             {
    39                 new ApiResource("gateway_api","user service")
    40             };
    41         }
    42     }
    Config

      编写我们的自定义自定义验证服务类,我们验证客户端传入的手机号&验证码是否正确(Demo逻辑中只需要填写正确手机号就可以了)

     1     public class SmsAuthCodeGrantType : IExtensionGrantValidator
     2     {
     3         private IUserService _userService;
     4         private IAuthCodeService _authCodeService;
     5         public SmsAuthCodeGrantType(IUserService userService, IAuthCodeService authCodeService)
     6         {
     7             _userService = userService;
     8             _authCodeService = authCodeService;
     9         }
    10         public string GrantType => "sms_auth_code";
    11         /// <summary>
    12         /// 
    13         /// </summary>
    14         /// <param name="context"></param>
    15         /// <returns></returns>
    16         public async Task ValidateAsync(ExtensionGrantValidationContext context)
    17         {
    18             var phone = context.Request.Raw["phone"];
    19             var code = context.Request.Raw["auth_code"];
    20             var errorValidationResult = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
    21 
    22 
    23             if (string.IsNullOrWhiteSpace(phone) || string.IsNullOrWhiteSpace(code))
    24             {
    25                 context.Result = errorValidationResult;
    26                 return;
    27             }
    28             //检查验证码
    29             if (!_authCodeService.Validate(phone, code))
    30             {
    31                 context.Result = errorValidationResult;
    32                 return;
    33             }
    34             //完成用户注册
    35             var userinfo = await _userService.CheckOrCreate(phone);
    36             if (userinfo== null)
    37             {
    38                 context.Result = errorValidationResult;
    39                 return;
    40             }
    41             var claims = new Claim[]
    42             {
    43                 new Claim("name",userinfo.Name??string.Empty),
    44                 new Claim("company",userinfo.Company??string.Empty),
    45                 new Claim("title",userinfo.Tiltle??string.Empty),
    46                 new Claim("avatar",userinfo.Avatar??string.Empty),
    47             }; 
    48             context.Result = new GrantValidationResult(userinfo.Id.ToString(), 
    49                 GrantType,
    50                 claims);
    51         }
    52     }
    SmsAuthCodeGrantType

      其他的验证服务和与User.API服务通信的服务类和返回的UserInfoDto

    1     public class UserInfo
    2     {
    3         public int Id { get; set; }
    4         public string Name { get; set; }
    5         public string Company { get; set; }
    6         public string Tiltle { get; set; }
    7         public string Avatar { get; set; }
    8     }
    UserInfo
     1     public interface IAuthCodeService
     2     {
     3         /// <summary>
     4         /// 根据手机号验证验证码
     5         /// </summary>
     6         /// <param name="phone"></param>
     7         /// <param name="authCode"></param>
     8         /// <returns></returns>
     9         bool Validate(string phone, string authCode);
    10     }
    IAuthCodeService
    1     public class TestAuthCodeService : IAuthCodeService
    2     {
    3         public bool Validate(string phone, string authCode)
    4         {
    5             return true;
    6         }
    7     }
    TestAuthCodeService
    1     public interface IUserService
    2     {
    3         /// <summary>
    4         /// 检查手机号是否注册,未注册就注册
    5         /// </summary>
    6         /// <param name="phone"></param>
    7         Task<UserInfo> CheckOrCreate(string phone);
    8     }
    IUserService
     1     public class UserService : IUserService
     2     {
     3         private HttpClient _httpClient;
     4         private string _userServiceUrl = "http://localhost:18000";
     5         public UserService(HttpClient httpClient)
     6         {
     7             _httpClient = httpClient;
     8         }
     9 
    10         public async Task<UserInfo> CheckOrCreate(string phone)
    11         {
    12             var from = new Dictionary<string, string>
    13             {
    14                 { "phone",phone }
    15             };
    16             var content = new FormUrlEncodedContent(from);
    17             var response = await _httpClient.PostAsync(_userServiceUrl + "/api/users/check_or_create", content);
    18             if (response.StatusCode == System.Net.HttpStatusCode.OK)
    19             {
    20                 var result = await response.Content.ReadAsStringAsync();
    21                 var userinfo = JsonConvert.DeserializeObject<UserInfo>(result);
    22 
    23                 //int.TryParse(userId,out int UserIdInt);
    24                 return userinfo;
    25             }
    26             return null;
    27         }
    28     }
    UserService

      配置Startup,注意要在我们的DI容器中注入自定义服务验证类(SmsAuthCodeGrantType)

     1     public class Startup
     2     {
     3         public Startup(IConfiguration configuration)
     4         {
     5             Configuration = configuration;
     6         }
     7 
     8         public IConfiguration Configuration { get; }
     9 
    10         // This method gets called by the runtime. Use this method to add services to the container.
    11         public void ConfigureServices(IServiceCollection services)
    12         {
    13             services.AddMvc();
    14             services.AddIdentityServer()
    15                 .AddExtensionGrantValidator<SmsAuthCodeGrantType>()  
    16                 .AddDeveloperSigningCredential()
    17                 .AddInMemoryClients(Config.GetClients())
    18                 .AddInMemoryIdentityResources(Config.GetIdentityResources())
    19                 .AddInMemoryApiResources(Config.GetApiResources());  //identityserver 认证
    20 
    21             services.AddScoped<IAuthCodeService, TestAuthCodeService>()
    22                 .AddScoped<IUserService, UserService>();
    23             services.AddSingleton(new HttpClient());
    24         }
    25 
    26         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    27         public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    28         {
    29             if (env.IsDevelopment())
    30             {
    31                 app.UseDeveloperExceptionPage();
    32             }
    33             app.UseIdentityServer();
    34             app.UseMvc();
    35         }
    36     }
    Startup
    • 最后配置我们的网关Ocelot站点,首先添加nuget包IdentityServer4.AccessTokenValidation和Ocelot。添加配置文件ocelot.json,其实就是博客开头的配置文件截图,这里特别说明下AuthenticationOptions节点,AuthenticationOptions是ocelot集成Identity所需要配置节点,AuthenticationProviderKey需要跟startup的authenticationScheme匹配
     1 {
     2   "ReRoutes": [
     3     {
     4       "DownstreamPathTemplate": "/api/users",
     5       "DownstreamScheme": "http",
     6       "DownstreamHostAndPorts": [
     7         {
     8           "Host": "localhost",
     9           "Port": 18000
    10         }
    11       ],
    12       "UpstreamPathTemplate": "/users",
    13       "UpstreamHttpMethod": [ "Get" ],
    14       "AuthenticationOptions": {
    15         "AuthenticationProviderKey": "finbook",
    16         "AllowedScopes": []
    17       }
    18     },
    19     {
    20       "DownstreamPathTemplate": "/connect/token",
    21       "DownstreamScheme": "http",
    22       "DownstreamHostAndPorts": [
    23         {
    24           "Host": "localhost",
    25           "Port": 18001
    26         }
    27       ],
    28       "UpstreamPathTemplate": "/connect/token",
    29       "UpstreamHttpMethod": [ "Post" ]
    30     }
    31   ],
    32   "GlobalConfiguration": {
    33     "BaseUrl": "http://localhost:18002"
    34   }
    35 }
    ocelot.json

    将配置文件加载到服务中,修改Program的CreateWebHostBuilder方法

     1     public class Program
     2     {
     3         public static void Main(string[] args)
     4         {
     5             CreateWebHostBuilder(args).Build().Run();
     6         }
     7 
     8         public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
     9             WebHost.CreateDefaultBuilder(args)
    10                 .UseStartup<Startup>()
    11                 .UseContentRoot(Directory.GetCurrentDirectory())
    12                 .ConfigureAppConfiguration((hostingContext, config) =>
    13                 {
    14                     config
    15                        .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
    16                        //.AddJsonFile("appsettings.json", true, true)
    17                        //.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
    18                        .AddJsonFile("ocelot.json")
    19                        .AddEnvironmentVariables();
    20                 })
    21                 .UseUrls("http://+:18002");
    22     }
    Program

    配置startup,在DI容器中加入Identity自定义认证,加入Ocelot,启用Ocelot中间件

     1     public class Startup
     2     {
     3         public Startup(IConfiguration configuration)
     4         {
     5             Configuration = configuration;
     6         }
     7 
     8         public IConfiguration Configuration { get; }
     9 
    10         // This method gets called by the runtime. Use this method to add services to the container.
    11         public void ConfigureServices(IServiceCollection services)
    12         {
    13             services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    14             //添加 认证信息
    15             var authenticationProviderKey = "finbook";
    16             services.AddAuthentication()
    17                 .AddIdentityServerAuthentication(authenticationProviderKey, options =>
    18                 {
    19                     options.Authority = "http://localhost:18001";
    20                     options.ApiName = "gateway_api";
    21                     options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Both;
    22                     options.ApiSecret = "secret";
    23                     options.RequireHttpsMetadata = false;
    24                 });
    25             services.AddOcelot();
    26         }
    27 
    28         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    29         public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    30         {
    31             if (env.IsDevelopment())
    32             {
    33                 app.UseDeveloperExceptionPage();
    34             }
    35             else
    36             {
    37                 // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    38                 app.UseHsts();
    39             }
    40             //app.UseAuthentication();
    41             app.UseOcelot(); //.Wait()
    42             app.UseHttpsRedirection();
    43             app.UseMvc();
    44         }
    45     }
    Startup
    • 验证运行

    首先获取token,访问ocelot网关的/connect/token地址,转发到Idnetity服务,注意下grant_type参数要和Identity服务中的配置相同

    接下来根据获取到的token,请求用户信息

    Demo地址=》https://github.com/madeinchinalmc/User.Api

  • 相关阅读:
    Python 实践
    Keras实践
    NLP S实践
    Spark java 实践
    Seaborn数据探索可视化
    Linux实践
    Redis
    ML算法选型
    Elasticsearch issue
    牛客练习赛37
  • 原文地址:https://www.cnblogs.com/liumengchen-boke/p/11223377.html
Copyright © 2020-2023  润新知