Loading

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

posted @ 2019-07-21 23:32  3WLineCode  阅读(1310)  评论(0编辑  收藏  举报