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. (1)ASP.NET Core3.1 Ocelot介绍

    1.简介 Ocelot原本设计仅为与.NET Core一起使用的,它是一个.NET API网关,作为面向使用.NET运行微型服务/面向服务的体系结构需要统一的系统入口点,即当客户端(Web站点,手机A ...

  2. (8)ASP.NET Core3.1 Ocelot Consul服务注册与发现

    1.服务注册与发现(Service Discovery) ●服务注册:我们通过在每个服务实例写入注册代码,实例在启动的时候会先去注册中心(例如Consul.ZooKeeper.etcd.Eureka) ...

  3. (2)ASP.NET Core3.1 Ocelot路由

    1.路由 前一个章节我们已经介绍过Ocelot,相信大家也了解到,Ocelot的主要功能是接收客户端等传入的HTTP请求,并将其转发到下游服务.Ocelot当前仅以另一个http请求的形式支持此功能( ...

  4. (4)ASP.NET Core3.1 Ocelot负载均衡

    1.负载均衡 Ocelot可以在每个路由的可用下游服务中实现负载均衡,这使我们更有效地选择下游服务来处理请求.负载均衡类型:●LeastConnection:根据服务正在处理请求量的情况来决定哪个服务 ...

  5. (5)ASP.NET Core3.1 Ocelot服务质量

    1.服务质量(Quality of Service) 对于微服务来说,熔断就是我们常说的"保险丝",意思是当服务出现某些状况时候,通过切断服务防止应用程序不断地执行可能会失败的操作 ...

  6. (7)ASP.NET Core3.1 Ocelot Swagger

    1.前言 前端与后端的联系更多是通过API接口对接,API文档变成了前后端开发人员联系的纽带,开始变得越来越重要,而Swagger就是一款让你更好的书写规范API文档的框架.在Ocelot Swagg ...

  7. ASP.NET Core3.1使用IdentityServer4中间件系列随笔(二):创建API项目,配置IdentityServer保护API资源

    配套源码:https://gitee.com/jardeng/IdentitySolution 接上一篇<ASP.NET Core3.1使用IdentityServer4中间件系列随笔(一):搭 ...

  8. Asp.Net Identity 2.0 认证

    转Asp.Net Identity 2.0 认证 一个星期前,也就是3月20日,微软发布了Asp.Net Identity 2.0 RTM.功能更加强大,也更加稳定.Identity这个东西现在版本还 ...

  9. asp.net core 2.1认证

    asp.net core 2.1认证 这篇文章基于asp.net core的CookieAuthenticationHandler来讲述. 认证和授权很相似,他们的英文也很相似,一个是Authenti ...

随机推荐

  1. 图像sensor的bitdepth

    参考来源:https://blog.csdn.net/yuejisuo1948/article/details/83617359 bitdepth目前个人理解是sensor像素上表示颜色的范围,也可说 ...

  2. Scala小记(一)

    Scala小记----初识Scala 一,什么是Scale? Scala是一门面向对象的,使用JVM运行的函数式编程语言,(函数式编程语言:指的就是那些将方法或者说是函数来作为参数 进行传递的编程语言 ...

  3. install Wine + WeChat in Fedora 31

    install Wine + WeChat in Fedora 31 dnf -y install dnf-plugins-core dnf config-manager --add-repo htt ...

  4. 一个故事看懂AI神经网络工作原理

    我是一个AI神经元 我是一个AI神经元,刚刚来到这个世界上,一切对我来说都特别新奇. 之所以叫这个名字,是因为我的工作有点像人类身体中的神经元. 人体中的神经元可以传递生物信号,给它输入一个信号,它经 ...

  5. Oracle误操作 数据恢复

    SELECT * FROM v$sqlarea //查询最近sql记录 SELECT r.FIRST_LOAD_TIME,r.* FROM v$sqlarea r ORDER BY r.FIRST_L ...

  6. 手工实现docker的vxlan

    前几天了解了一下docker overlay的原理,然后一直想验证一下自己的理解是否正确,今天模仿docker手工搭建了一个overlay网络.先上拓扑图,其实和上次画的基本一样.我下面提到的另一台机 ...

  7. 利用Turtle绘制各种图形

    首先引入函数库: 第一种: import turtle  import turtle as t 第二种: from turtle import * 1:使用 turtle 库的 turtle.fd() ...

  8. 【转载】opencvVS2019配置方法

    环境: 系统:win10系统截至2020920版本 opencv版本:3.0.0版本 IDE:宇宙最强IDEA最新版本2019社区版 教程: 1.下载opencv安装包官网下载链接:https://o ...

  9. html学习(1)

    认识html标签 1.<h1></h1>就是标题标签,<p></p>是段落标签,img是图片标签.  2.html中的标签一般都是成对出现的,分开始标签 ...

  10. ScanTailor-ScanTailor 强大的多方位的满足处理扫描图片的需求

    ScanTailor 强大的多方位的满足处理扫描图片的需求   ScanTailor 能做什么? 批量或单张或选择区间旋转图片 自动切割页面,同时提供手动选项 自动识别图像歪斜角度,同时提供手动选项 ...