asp.net core網關Ocelot的簡單介紹& Ocelot集成Identity認證

文章簡介

  •  Ocelot網關簡介

  •  Ocelot集成Idnetity認證處理

Ocelot網關簡介

Ocelot是一個基於netcore實現的API網關,本質是一組按特定順序排列的中間件。Ocelot內部實現了路由轉發,限流,熔斷,請求聚合,服務發現(集成consul,eureka等),負載均衡,認證(集成Identity)功能。html

這裏簡單介紹下ocelot的配置文件,也就是說如下圖爲例,請求地址爲localhost:18002/users會被轉發到localhost:18000/api/usersandroid

更多關於Ocelot的介紹能夠看https://www.cnblogs.com/jesse2013/p/net-core-apigateway-ocelot-docs.html這篇博客或者https://ocelot.readthedocs.io/en/latest/index.html官方文檔。ios

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=IExtensionGrantValidatorgit

 

 首先咱們建立三個服務,分別爲Ocelot網關服務(端口號設置爲18002),Identity認證服務(端口號設置爲18001),UserInfo用戶信息服務(端口號設置爲18000),以下圖所示=》github

 

  • 首先咱們配置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邏輯中只須要填寫正確手機號就能夠了)json

 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服務通訊的服務類和返回的UserInfoDtoapi

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)app

 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中間件async

 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

相關文章
相關標籤/搜索