(3)ASP.NET Core3.1 Ocelot认证
1.认证
当客户端通过Ocelot访问下游服务的时候,为了保护下游资源服务器会进行认证鉴权,这时候需要在Ocelot添加认证服务。添加认证服务后,随后使用Ocelot基于声明的任何功能,例如授权或使用Token中的值修改请求。用户必须像往常一样在其Startup.cs中注册身份验证服务,但是他们为每次注册提供一个方案(身份验证提供者密钥),例如:
- public void ConfigureServices(IServiceCollection services)
- {
- var authenticationProviderKey = "TestKey";
- services.AddAuthentication()
- .AddJwtBearer(authenticationProviderKey, x =>
- {
- });
- }
在此Ocelot认证项目示例中,TestKey是已注册此提供程序的方案。然后我们将其映射到配置中的Routes路由,例如:
- {
- "Routes": [
- {
- "DownstreamPathTemplate": "/api/customers",
- "DownstreamScheme": "http",
- "DownstreamHost": "localhost",
- "DownstreamPort": 9001,
- "UpstreamPathTemplate": "/customers",
- "UpstreamHttpMethod": [ "Get" ],
- "AuthenticationOptions": {
- "AuthenticationProviderKey": "TestKey",
- "AllowedScopes": []
- }
- }
- ]
- }
Ocelot运行时,它将查看Routes.AuthenticationOptions.AuthenticationProviderKey并检查是否存在使用给定密钥注册的身份验证提供程序。如果不存在,则Ocelot将不会启动,如果存在,则Routes将在执行时使用该提供程序。
如果对路由进行身份验证,Ocelot将在执行身份验证中间件时调用与之关联的任何方案。如果请求通过身份验证失败,Ocelot将返回http状态代码401。
2.JWT Tokens Bearer认证
Json Web Token (JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519)。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
2.1JWT令牌结构
在紧凑的形式中,JSON Web Tokens由dot(.)分隔的三个部分组成,它们是:
Header头、Payload有效载荷、Signature签名
因此,JWT通常如下所示:xxxxx.yyyyy.zzzzz(Header.Payload.Signature)
2.1.1Header头
标头通常由两部分组成:令牌的类型,即JWT,以及正在使用的签名算法,例如HMAC SHA256或RSA。例如:
- {
- "alg": "HS256",
- "typ": "JWT"
- }
然后,这个JSON被编码为Base64Url,形成JWT的第一部分。
2.1.2Payload有效载荷
Payload部分也是一个JSON对象,用来存放实际需要传递的数据。JWT规定了7个官方字段,供选用。
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。例如:
- {
- "sub": "1234567890",
- "name": "John Doe",
- "admin": true
- }
注意,JWT默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。这个JSON对象也要使用Base64URL算法转成字符串。
2.1.3.Signature签名
Signature部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用Header里面指定的签名算法(默认是HMAC SHA256),按照下面的公式产生签名。
- HMACSHA256(
- base64UrlEncode(header) + "." +
- base64UrlEncode(payload),
- secret)
签名用于验证消息在此过程中未被更改,并且,在使用私钥签名的令牌的情况下,它还可以验证JWT的发件人是否是它所声称的人。
把他们三个全部放在一起,输出是三个由点分隔的Base64-URL字符串,可以在HTML和HTTP环境中轻松传递,而与基于XML的标准(如SAML)相比更加紧凑。
下面显示了一个JWT,它具有先前的头和有效负载编码,并使用机密签名。
- eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
- .eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoid3prNzAzIiwibmJmIjoiMTU5MjE0MzkzNyIsImV4cCI6MTU5MjE0Mzk5OCwiaXNzIjoiYXV0aC5qd3QuY2MiLCJhdWQiOiJkZW5nd3V8MjAyMC82LzE0IDIyOjEyOjE5In0
- .4RiwhRy0rQkZjclOFWyTpmW7v0AMaL3aeve1L-eWIz0
其实一般发送用户名和密码获取token那是由Identity4来完成的,包括验证用户,生成JwtToken。但是项目这里是由System.IdentityModel.Tokens类库来生成JwtToken。最后返回jwt令牌token给用户。JwtToken解码可以通过https://jwt.io/中进行查看。
3.项目演示
3.1APIGateway项目
在该项目中启用身份认证来保护下游api服务,使用JwtBearer认证,将默认的身份验证方案设置为TestKey。在appsettings.json文件中配置认证中密钥(Secret)跟受众(Aud)信息:
- {
- "Audience": {
- "Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==",
- "Iss": "http://www.c-sharpcorner.com/members/catcher-wong",
- "Aud": "Catcher Wong"
- }
- }
Startup添加身份认证代码如下:
- public void ConfigureServices(IServiceCollection services)
- {
- //获取appsettings.json文件中配置认证中密钥(Secret)跟受众(Aud)信息
- var audienceConfig = Configuration.GetSection("Audience");
- //获取安全秘钥
- var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(audienceConfig["Secret"]));
- //token要验证的参数集合
- var tokenValidationParameters = new TokenValidationParameters
- {
- //必须验证安全秘钥
- ValidateIssuerSigningKey = true,
- //赋值安全秘钥
- IssuerSigningKey = signingKey,
- //必须验证签发人
- ValidateIssuer = true,
- //赋值签发人
- ValidIssuer = audienceConfig["Iss"],
- //必须验证受众
- ValidateAudience = true,
- //赋值受众
- ValidAudience = audienceConfig["Aud"],
- //是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
- ValidateLifetime = true,
- //允许的服务器时间偏移量
- ClockSkew = TimeSpan.Zero,
- //是否要求Token的Claims中必须包含Expires
- RequireExpirationTime = true,
- };
- //添加服务验证,方案为TestKey
- services.AddAuthentication(o =>
- {
- o.DefaultAuthenticateScheme = "TestKey";
- })
- .AddJwtBearer("TestKey", x =>
- {
- x.RequireHttpsMetadata = false;
- //在JwtBearerOptions配置中,IssuerSigningKey(签名秘钥)、ValidIssuer(Token颁发机构)、ValidAudience(颁发给谁)三个参数是必须的。
- x.TokenValidationParameters = tokenValidationParameters;
- });
- //添加Ocelot网关服务时,包括Secret秘钥、Iss签发人、Aud受众
- services.AddOcelot(Configuration);
- }
- public async void Configure(IApplicationBuilder app, IHostingEnvironment env)
- {
- //使用认证服务
- app.UseAuthentication();
- //使用Ocelot中间件
- await app.UseOcelot();
- }
3.1.1Identity Server承载JWT Token
在第二小节介绍JWT Token认证时候,我们都知道一般发送用户名和密码获取Token那是由Identity4来完成的,包括验证用户,生成JWT Token。也就是说Identity Server承载了JWT Token认证功能。为了使用IdentityServer承载Token,请像往常一样在ConfigureServices中使用方案(密钥)注册IdentityServer服务。如果您不知道如何执行此操作,请查阅IdentityServer文档。
- public void ConfigureServices(IServiceCollection services)
- {
- var authenticationProviderKey = "TestKey";
- Action<IdentityServerAuthenticationOptions> options = o =>
- {
- o.Authority = "https://whereyouridentityserverlives.com";
- o.ApiName = "api";
- o.SupportedTokens = SupportedTokens.Both;
- o.ApiSecret = "secret";
- };
- services.AddAuthentication()
- .AddIdentityServerAuthentication(authenticationProviderKey, options);
- services.AddOcelot();
- }
在Identity4中是由Authority参数指定OIDC服务地址,OIDC可以自动发现Issuer, IssuerSigningKey等配置,而o.Audience与x.TokenValidationParameters = new TokenValidationParameters { ValidAudience = "api" }是等效的。
3.2AuthServer项目
此服务主要用于客户端请求受保护的资源服务器时,认证后产生客户端需要的JWT Token,生成JWT Token关键代码如下:
- [Route("api/[controller]")]
- public class AuthController : Controller
- {
- private IOptions<Audience> _settings;
- public AuthController(IOptions<Audience> settings)
- {
- this._settings = settings;
- }
- /// <summary>
- ///用户使用 用户名密码 来请求服务器
- ///服务器进行验证用户的信息
- ///服务器通过验证发送给用户一个token
- ///客户端存储token,并在每次请求时附送上这个token值, headers: {'Authorization': 'Bearer ' + token}
- ///服务端验证token值,并返回数据
- /// </summary>
- /// <param name="name"></param>
- /// <param name="pwd"></param>
- /// <returns></returns>
- [HttpGet]
- public IActionResult Get(string name, string pwd)
- {
- //验证登录用户名和密码
- if (name == "catcher" && pwd == "123")
- {
- var now = DateTime.UtcNow;
- //添加用户的信息,转成一组声明,还可以写入更多用户信息声明
- var claims = new Claim[]
- {
- //声明主题
- new Claim(JwtRegisteredClaimNames.Sub, name),
- //JWT ID 唯一标识符
- new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
- //发布时间戳 issued timestamp
- new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64)
- };
- //下面使用 Microsoft.IdentityModel.Tokens帮助库下的类来创建JwtToken
- //安全秘钥
- var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_settings.Value.Secret));
- //声明jwt验证参数
- var tokenValidationParameters = new TokenValidationParameters
- {
- //必须验证安全秘钥
- ValidateIssuerSigningKey = true,
- //赋值安全秘钥
- IssuerSigningKey = signingKey,
- //必须验证签发人
- ValidateIssuer = true,
- //赋值签发人
- ValidIssuer = _settings.Value.Iss,
- //必须验证受众
- ValidateAudience = true,
- //赋值受众
- ValidAudience = _settings.Value.Aud,
- //是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
- ValidateLifetime = true,
- //允许的服务器时间偏移量
- ClockSkew = TimeSpan.Zero,
- //是否要求Token的Claims中必须包含Expires
- RequireExpirationTime = true,
- };
- var jwt = new JwtSecurityToken(
- //jwt签发人
- issuer: _settings.Value.Iss,
- //jwt受众
- audience: _settings.Value.Aud,
- //jwt一组声明
- claims: claims,
- notBefore: now,
- //jwt令牌过期时间
- expires: now.Add(TimeSpan.FromMinutes(2)),
- //签名凭证: 安全密钥、签名算法
- signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)
- );
- //生成jwt令牌(json web token)
- var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
- var responseJson = new
- {
- access_token = encodedJwt,
- expires_in = (int)TimeSpan.FromMinutes(2).TotalSeconds
- };
- return Json(responseJson);
- }
- else
- {
- return Json("");
- }
- }
- }
- public class Audience
- {
- public string Secret { get; set; }
- public string Iss { get; set; }
- public string Aud { get; set; }
- }
appsettings.json文件中配置认证中密钥(Secret)跟受众(Aud)信息:
- {
- "Audience": {
- "Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==",
- "Iss": "http://www.c-sharpcorner.com/members/catcher-wong",
- "Aud": "Catcher Wong"
- }
- }
3.3CustomerAPIServices项目
该项目跟APIGateway项目是一样的,为了保护下游api服务,使用JwtBearer认证,将默认的身份验证方案设置为TestKey。在appsettings.json文件中配置认证中密钥(Secret)跟受众(Aud)信息:
- {
- "Audience": {
- "Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==",
- "Iss": "http://www.c-sharpcorner.com/members/catcher-wong",
- "Aud": "Catcher Wong"
- }
- }
Startup添加身份认证代码如下:
- public void ConfigureServices(IServiceCollection services)
- {
- //获取appsettings.json文件中配置认证中密钥(Secret)跟受众(Aud)信息
- var audienceConfig = Configuration.GetSection("Audience");
- //获取安全秘钥
- var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(audienceConfig["Secret"]));
- //token要验证的参数集合
- var tokenValidationParameters = new TokenValidationParameters
- {
- //必须验证安全秘钥
- ValidateIssuerSigningKey = true,
- //赋值安全秘钥
- IssuerSigningKey = signingKey,
- //必须验证签发人
- ValidateIssuer = true,
- //赋值签发人
- ValidIssuer = audienceConfig["Iss"],
- //必须验证受众
- ValidateAudience = true,
- //赋值受众
- ValidAudience = audienceConfig["Aud"],
- //是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
- ValidateLifetime = true,
- //允许的服务器时间偏移量
- ClockSkew = TimeSpan.Zero,
- //是否要求Token的Claims中必须包含Expires
- RequireExpirationTime = true,
- };
- //添加服务验证,方案为TestKey
- services.AddAuthentication(o =>
- {
- o.DefaultAuthenticateScheme = "TestKey";
- })
- .AddJwtBearer("TestKey", x =>
- {
- x.RequireHttpsMetadata = false;
- //在JwtBearerOptions配置中,IssuerSigningKey(签名秘钥)、ValidIssuer(Token颁发机构)、ValidAudience(颁发给谁)三个参数是必须的。
- x.TokenValidationParameters = tokenValidationParameters;
- });
- services.AddMvc();
- }
- public void Configure(IApplicationBuilder app)
- {
- //使用认证服务
- app.UseAuthentication();
- app.UseMvc();
- }
在CustomersController下添加一个需要认证方法,一个不需要认证方法:
- [Route("api/[controller]")]
- public class CustomersController : Controller
- {
- //添加认证属性
- [Authorize]
- [HttpGet]
- public IEnumerable<string> Get()
- {
- return new string[] { "Catcher Wong", "James Li" };
- }
- [HttpGet("{id}")]
- public string Get(int id)
- {
- return $"Catcher Wong - {id}";
- }
- }
3.4ClientApp项目
该项目是用来模拟客户端访问资源服务器整个认证流程测试项目,在Program主程序可以看到如下代码:
- class Program
- {
- static void Main(string[] args)
- {
- HttpClient client = new HttpClient();
- client.DefaultRequestHeaders.Clear();
- client.BaseAddress = new Uri("http://localhost:9000");
- // 1. without access_token will not access the service
- // and return 401 .
- var resWithoutToken = client.GetAsync("/customers").Result;
- Console.WriteLine($"Sending Request to /customers , without token.");
- Console.WriteLine($"Result : {resWithoutToken.StatusCode}");
- //2. with access_token will access the service
- // and return result.
- client.DefaultRequestHeaders.Clear();
- Console.WriteLine("\nBegin Auth....");
- var jwt = GetJwt();
- Console.WriteLine("End Auth....");
- Console.WriteLine($"\nToken={jwt}");
- client.DefaultRequestHeaders.Add("Authorization", $"Bearer {jwt}");
- var resWithToken = client.GetAsync("/customers").Result;
- Console.WriteLine($"\nSend Request to /customers , with token.");
- Console.WriteLine($"Result : {resWithToken.StatusCode}");
- Console.WriteLine(resWithToken.Content.ReadAsStringAsync().Result);
- //3. visit no auth service
- Console.WriteLine("\nNo Auth Service Here ");
- client.DefaultRequestHeaders.Clear();
- var res = client.GetAsync("/customers/1").Result;
- Console.WriteLine($"Send Request to /customers/1");
- Console.WriteLine($"Result : {res.StatusCode}");
- Console.WriteLine(res.Content.ReadAsStringAsync().Result);
- Console.Read();
- }
- private static string GetJwt()
- {
- HttpClient client = new HttpClient();
- client.BaseAddress = new Uri( "http://localhost:9000");
- client.DefaultRequestHeaders.Clear();
- var res2 = client.GetAsync("/api/auth?name=catcher&pwd=123").Result;
- dynamic jwt = JsonConvert.DeserializeObject(res2.Content.ReadAsStringAsync().Result);
- return jwt.access_token;
- }
- }
运行项目看看测试结果:
结合代码,我们能看到当客户端通过Ocelot网关访问下游服务http://localhost:9000/api/Customers/Get方法时候,因为该方法是需要通过认证才返回处理结果的,所以会进行JWT Token认证,如果发现没有Token,Ocelot则返回http状态代码401拒绝访问。如果我们通过GetJwt方法在AuthServer服务上登录认证获取到授权Token,然后再访问该资源服务器接口,立即就会返回处理结果,通过跟而未加认证属性的http://localhost:9000/api/Customers/Get/{id}方法对比,我们就知道,Ocelot认证已经成功了!
4.总结
该章节只是结合demo项目简单介绍在Ocelot中如何使用JWT Token认证。其实正式环境中,Ocelot是应该集成IdentityServer认证授权的,同样的通过重写Ocelot中间件我们还可以把configuration.json的配置信息存储到数据库或者缓存到Redis中。
参考文献:
Ocelot官网
(3)ASP.NET Core3.1 Ocelot认证的更多相关文章
- (1)ASP.NET Core3.1 Ocelot介绍
1.简介 Ocelot原本设计仅为与.NET Core一起使用的,它是一个.NET API网关,作为面向使用.NET运行微型服务/面向服务的体系结构需要统一的系统入口点,即当客户端(Web站点,手机A ...
- (8)ASP.NET Core3.1 Ocelot Consul服务注册与发现
1.服务注册与发现(Service Discovery) ●服务注册:我们通过在每个服务实例写入注册代码,实例在启动的时候会先去注册中心(例如Consul.ZooKeeper.etcd.Eureka) ...
- (2)ASP.NET Core3.1 Ocelot路由
1.路由 前一个章节我们已经介绍过Ocelot,相信大家也了解到,Ocelot的主要功能是接收客户端等传入的HTTP请求,并将其转发到下游服务.Ocelot当前仅以另一个http请求的形式支持此功能( ...
- (4)ASP.NET Core3.1 Ocelot负载均衡
1.负载均衡 Ocelot可以在每个路由的可用下游服务中实现负载均衡,这使我们更有效地选择下游服务来处理请求.负载均衡类型:●LeastConnection:根据服务正在处理请求量的情况来决定哪个服务 ...
- (5)ASP.NET Core3.1 Ocelot服务质量
1.服务质量(Quality of Service) 对于微服务来说,熔断就是我们常说的"保险丝",意思是当服务出现某些状况时候,通过切断服务防止应用程序不断地执行可能会失败的操作 ...
- (7)ASP.NET Core3.1 Ocelot Swagger
1.前言 前端与后端的联系更多是通过API接口对接,API文档变成了前后端开发人员联系的纽带,开始变得越来越重要,而Swagger就是一款让你更好的书写规范API文档的框架.在Ocelot Swagg ...
- ASP.NET Core3.1使用IdentityServer4中间件系列随笔(二):创建API项目,配置IdentityServer保护API资源
配套源码:https://gitee.com/jardeng/IdentitySolution 接上一篇<ASP.NET Core3.1使用IdentityServer4中间件系列随笔(一):搭 ...
- Asp.Net Identity 2.0 认证
转Asp.Net Identity 2.0 认证 一个星期前,也就是3月20日,微软发布了Asp.Net Identity 2.0 RTM.功能更加强大,也更加稳定.Identity这个东西现在版本还 ...
- asp.net core 2.1认证
asp.net core 2.1认证 这篇文章基于asp.net core的CookieAuthenticationHandler来讲述. 认证和授权很相似,他们的英文也很相似,一个是Authenti ...
随机推荐
- JavaFX ComboBox的选中事项
参考1:https://blog.csdn.net/mexel310/article/details/37909205 参考2:https://blog.csdn.net/maosijunzi/art ...
- 安装redis,phpstudy
第一步: 先下载一下我分享自己的百度网盘里面的资料,网友们下载即可 链接:https://pan.baidu.com/s/1SVO-yAEqbFuvhiiI6Dm3VQ 提取码:u8t0 复制这段内容 ...
- 多测师讲解自动化测试 _RF关键字001_( 中)_高级讲师肖sir
1.关键字如下 1.1Get Text 1.2Get Value 2.#上下滑动(滚动条) Open Browser http://www.jd.com gc Maximize Browser Win ...
- 框架-设备与驱动的拆分及实现-I2C
目录 前言 笔录草稿 概要 原理及实现方法 IIC 例子实战-驱动 1. 创建文件 2. 创建 I2C 驱动名字列表 3. 组建 I2C 驱动结构体 4. 编写-注册 I2C 驱动函数 5. 创建 I ...
- kubernetes:用kubeadm管理token(kubernetes 1.18.3)
一,token的用途: 1,token是node节点用来连接master节点的令牌字串, 它和ca证书的hash值是把一台node节点加入到kubernetes集群时要使用的凭证 2, 通过kubea ...
- selenium基础 --获取内容
from time import sleep from selenium import webdriver browser = webdriver.Chrome() url = "http: ...
- centOS7永久关闭防火墙(防火墙的基本使用(转)
查看防火墙状态: systemctl status firewalld.service 如图 绿的running表示防火墙开启 执行关闭命令: systemctl stop firewalld.ser ...
- pdf 转word 工具
在线转换,每天有次数限制,但是很强大: https://smallpdf.com/cn 可以使用python 写代码来转换文档 参考下面博客连接 https://blog.csdn.net/Dontl ...
- Linux用户和组管理命令-切换用户su
切换用户或以其他用户身份执行命令 su: 即 switch user,命令可以切换用户身份,并且以指定用户的身份执行命令 格式: su [options...] [-] [user [args...] ...
- SQL DELETE语句如何让表使用别名的方法
DELETE 别名 FROM 表名称 别名 WHERE 列名称 = 值