ASP.NET Core3.1 Ocelot認(rèn)證的實現(xiàn)
1.認(rèn)證
當(dāng)客戶端通過Ocelot訪問下游服務(wù)的時候,為了保護(hù)下游資源服務(wù)器會進(jìn)行認(rèn)證鑒權(quán),這時候需要在Ocelot添加認(rèn)證服務(wù)。添加認(rèn)證服務(wù)后,隨后Ocelot會基于授權(quán)密鑰授權(quán)每個請求可以訪問的資源。用戶必須像往常一樣在其Startup.cs中注冊身份驗證服務(wù),但是他們?yōu)槊看巫蕴峁┮粋€方案(身份驗證提供者密鑰),例如:
public void ConfigureServices(IServiceCollection services)
{
var authenticationProviderKey = "TestKey";
services.AddAuthentication()
.AddJwtBearer(authenticationProviderKey, x =>
{
});
}
在此Ocelot認(rèn)證項目示例中,TestKey是已注冊此提供程序的方案,然后將其映射到網(wǎng)關(guān)項目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將在執(zhí)行時使用該提供程序。如果對路由進(jìn)行身份驗證,Ocelot將在執(zhí)行身份驗證中間件時調(diào)用與之關(guān)聯(lián)的任何方案。如果請求通過身份驗證失敗,Ocelot將返回http狀態(tài)代碼401。
2.JWT Tokens Bearer認(rèn)證
Json Web Token (JWT),是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標(biāo)準(zhǔn)(RFC 7519)。該token被設(shè)計為緊湊且安全的,特別適用于分布式站點的單點登錄(SSO)場景。JWT的聲明一般被用來在身份提供者和服務(wù)提供者間傳遞被認(rèn)證的用戶身份信息,以便于從資源服務(wù)器獲取資源,也可以增加一些額外的其它業(yè)務(wù)邏輯所必須的聲明信息,該token也可直接被用于認(rèn)證,也可被加密。

2.1JWT令牌結(jié)構(gòu)
在緊湊的形式中,JSON Web Tokens由dot(.)分隔的三個部分組成,它們是:Header頭、Payload有效載荷、Signature簽名。因此,JWT通常如下所示:xxxxx.yyyyy.zzzzz(Header.Payload.Signature)。
2.1.1Header頭
標(biāo)頭通常由兩部分組成:令牌的類型,即JWT,以及正在使用的簽名算法,例如HMAC SHA256或RSA。例如:
{
"alg": "HS256",
"typ": "JWT"
}
然后,這個JSON被編碼為Base64Url,形成JWT的第一部分。
2.1.2Payload有效載荷
Payload部分也是一個JSON對象,用來存放實際需要傳遞的數(shù)據(jù)。JWT規(guī)定了7個官方字段,供選用。
- iss (issuer):簽發(fā)人
- exp (expiration time):過期時間
- sub (subject):主題
- aud (audience):受眾
- nbf (Not Before):生效時間
- iat (Issued At):簽發(fā)時間
- jti (JWT ID):編號
除了官方字段,你還可以在這個部分定義私有字段,下面就是一個例子。例如:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
注意,JWT默認(rèn)是不加密的,任何人都可以讀到,所以不要把秘密信息放在這個部分。這個JSON對象也要使用Base64URL算法轉(zhuǎn)成字符串。
2.1.3.Signature簽名
Signature部分是前兩部分的簽名,防止數(shù)據(jù)篡改。首先,需要指定一個密鑰(secret)。這個密鑰只有服務(wù)器才知道,不能泄露給用戶。然后,使用Header里面指定的簽名算法(默認(rèn)是HMAC SHA256),按照下面的公式產(chǎn)生簽名:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
簽名用于驗證消息在此過程中未被更改,并且,在使用私鑰簽名的令牌的情況下,它還可以驗證JWT的發(fā)件人是否是它所聲稱的人。把他們?nèi)齻€全部放在一起,輸出是三個由點分隔的Base64-URL字符串,可以在HTML和HTTP環(huán)境中輕松傳遞,而與基于XML的標(biāo)準(zhǔn)(如SAML)相比更加緊湊。下面顯示了一個JWT,它具有先前的頭和有效負(fù)載編碼,并使用機密簽名:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 .eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoid3prNzAzIiwibmJmIjoiMTU5MjE0MzkzNyIsImV4cCI6MTU5MjE0Mzk5OCwiaXNzIjoiYXV0aC5qd3QuY2MiLCJhdWQiOiJkZW5nd3V8MjAyMC82LzE0IDIyOjEyOjE5In0 .4RiwhRy0rQkZjclOFWyTpmW7v0AMaL3aeve1L-eWIz0
其實一般發(fā)送用戶名和密碼獲取token那是由Identity4來完成的,包括驗證用戶,生成JwtToken。但是項目這里是由System.IdentityModel.Tokens類庫來生成JwtToken。最后返回jwt令牌token給用戶。JwtToken解碼可以通過https://jwt.io/中進(jìn)行查看。
3.項目演示
3.1APIGateway項目
在該項目中啟用身份認(rèn)證來保護(hù)下游api服務(wù),使用JwtBearer認(rèn)證,將默認(rèn)的身份驗證方案設(shè)置為TestKey。在appsettings.json文件中配置認(rèn)證中密鑰(Secret)跟受眾(Aud)信息:
{
"Audience": {
"Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==",
"Iss": "http://www.c-sharpcorner.com/members/catcher-wong",
"Aud": "Catcher Wong"
}
}
Startup添加身份認(rèn)證代碼如下:
public void ConfigureServices(IServiceCollection services)
{
//獲取appsettings.json文件中配置認(rèn)證中密鑰(Secret)跟受眾(Aud)信息
var audienceConfig = Configuration.GetSection("Audience");
//獲取安全秘鑰
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(audienceConfig["Secret"]));
//token要驗證的參數(shù)集合
var tokenValidationParameters = new TokenValidationParameters
{
//必須驗證安全秘鑰
ValidateIssuerSigningKey = true,
//賦值安全秘鑰
IssuerSigningKey = signingKey,
//必須驗證簽發(fā)人
ValidateIssuer = true,
//賦值簽發(fā)人
ValidIssuer = audienceConfig["Iss"],
//必須驗證受眾
ValidateAudience = true,
//賦值受眾
ValidAudience = audienceConfig["Aud"],
//是否驗證Token有效期,使用當(dāng)前時間與Token的Claims中的NotBefore和Expires對比
ValidateLifetime = true,
//允許的服務(wù)器時間偏移量
ClockSkew = TimeSpan.Zero,
//是否要求Token的Claims中必須包含Expires
RequireExpirationTime = true,
};
//添加服務(wù)驗證,方案為TestKey
services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = "TestKey";
})
.AddJwtBearer("TestKey", x =>
{
x.RequireHttpsMetadata = false;
//在JwtBearerOptions配置中,IssuerSigningKey(簽名秘鑰)、ValidIssuer(Token頒發(fā)機構(gòu))、ValidAudience(頒發(fā)給誰)三個參數(shù)是必須的。
x.TokenValidationParameters = tokenValidationParameters;
});
//添加Ocelot網(wǎng)關(guān)服務(wù)時,包括Secret秘鑰、Iss簽發(fā)人、Aud受眾
services.AddOcelot(Configuration);
}
public async void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//使用認(rèn)證服務(wù)
app.UseAuthentication();
//使用Ocelot中間件
await app.UseOcelot();
}
3.1.1Identity Server承載JWT Token
在第二小節(jié)介紹JWT Token認(rèn)證時候,我們都知道一般發(fā)送用戶名和密碼獲取Token那是由Identity4來完成的,包括驗證用戶,生成JWT Token。也就是說Identity Server承載了JWT Token認(rèn)證功能。為了使用IdentityServer承載Token,請像往常一樣在ConfigureServices中使用方案(密鑰)注冊IdentityServer服務(wù)。如果您不知道如何執(zhí)行此操作,請查閱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參數(shù)指定OIDC服務(wù)地址,OIDC可以自動發(fā)現(xiàn)Issuer, IssuerSigningKey等配置,而o.Audience與x.TokenValidationParameters = new TokenValidationParameters { ValidAudience = "api" }是等效的。
3.2AuthServer項目
此服務(wù)主要用于客戶端請求受保護(hù)的資源服務(wù)器時,認(rèn)證后產(chǎn)生客戶端需要的JWT Token,生成JWT Token關(guān)鍵代碼如下:
[Route("api/[controller]")]
public class AuthController : Controller
{
private IOptions<Audience> _settings;
public AuthController(IOptions<Audience> settings)
{
this._settings = settings;
}
/// <summary>
///用戶使用 用戶名密碼 來請求服務(wù)器
///服務(wù)器進(jìn)行驗證用戶的信息
///服務(wù)器通過驗證發(fā)送給用戶一個token
///客戶端存儲token,并在每次請求時附送上這個token值, headers: {'Authorization': 'Bearer ' + token}
///服務(wù)端驗證token值,并返回數(shù)據(jù)
/// </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;
//添加用戶的信息,轉(zhuǎn)成一組聲明,還可以寫入更多用戶信息聲明
var claims = new Claim[]
{
//聲明主題
new Claim(JwtRegisteredClaimNames.Sub, name),
//JWT ID 唯一標(biāo)識符
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
//發(fā)布時間戳 issued timestamp
new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64)
};
//下面使用 Microsoft.IdentityModel.Tokens幫助庫下的類來創(chuàng)建JwtToken
//安全秘鑰
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_settings.Value.Secret));
//聲明jwt驗證參數(shù)
var tokenValidationParameters = new TokenValidationParameters
{
//必須驗證安全秘鑰
ValidateIssuerSigningKey = true,
//賦值安全秘鑰
IssuerSigningKey = signingKey,
//必須驗證簽發(fā)人
ValidateIssuer = true,
//賦值簽發(fā)人
ValidIssuer = _settings.Value.Iss,
//必須驗證受眾
ValidateAudience = true,
//賦值受眾
ValidAudience = _settings.Value.Aud,
//是否驗證Token有效期,使用當(dāng)前時間與Token的Claims中的NotBefore和Expires對比
ValidateLifetime = true,
//允許的服務(wù)器時間偏移量
ClockSkew = TimeSpan.Zero,
//是否要求Token的Claims中必須包含Expires
RequireExpirationTime = true,
};
var jwt = new JwtSecurityToken(
//jwt簽發(fā)人
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文件中配置認(rèn)證中密鑰(Secret)跟受眾(Aud)信息:
{
"Audience": {
"Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==",
"Iss": "http://www.c-sharpcorner.com/members/catcher-wong",
"Aud": "Catcher Wong"
}
}
3.3CustomerAPIServices項目
該項目跟APIGateway項目是一樣的,為了保護(hù)下游api服務(wù),使用JwtBearer認(rèn)證,將默認(rèn)的身份驗證方案設(shè)置為TestKey。在appsettings.json文件中配置認(rèn)證中密鑰(Secret)跟受眾(Aud)信息:
{
"Audience": {
"Secret": "Y2F0Y2hlciUyMHdvbmclMjBsb3ZlJTIwLm5ldA==",
"Iss": "http://www.c-sharpcorner.com/members/catcher-wong",
"Aud": "Catcher Wong"
}
}
Startup添加身份認(rèn)證代碼如下:
public void ConfigureServices(IServiceCollection services)
{
//獲取appsettings.json文件中配置認(rèn)證中密鑰(Secret)跟受眾(Aud)信息
var audienceConfig = Configuration.GetSection("Audience");
//獲取安全秘鑰
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(audienceConfig["Secret"]));
//token要驗證的參數(shù)集合
var tokenValidationParameters = new TokenValidationParameters
{
//必須驗證安全秘鑰
ValidateIssuerSigningKey = true,
//賦值安全秘鑰
IssuerSigningKey = signingKey,
//必須驗證簽發(fā)人
ValidateIssuer = true,
//賦值簽發(fā)人
ValidIssuer = audienceConfig["Iss"],
//必須驗證受眾
ValidateAudience = true,
//賦值受眾
ValidAudience = audienceConfig["Aud"],
//是否驗證Token有效期,使用當(dāng)前時間與Token的Claims中的NotBefore和Expires對比
ValidateLifetime = true,
//允許的服務(wù)器時間偏移量
ClockSkew = TimeSpan.Zero,
//是否要求Token的Claims中必須包含Expires
RequireExpirationTime = true,
};
//添加服務(wù)驗證,方案為TestKey
services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = "TestKey";
})
.AddJwtBearer("TestKey", x =>
{
x.RequireHttpsMetadata = false;
//在JwtBearerOptions配置中,IssuerSigningKey(簽名秘鑰)、ValidIssuer(Token頒發(fā)機構(gòu))、ValidAudience(頒發(fā)給誰)三個參數(shù)是必須的。
x.TokenValidationParameters = tokenValidationParameters;
});
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
//使用認(rèn)證服務(wù)
app.UseAuthentication();
app.UseMvc();
}
在CustomersController下添加一個需要認(rèn)證方法,一個不需要認(rèn)證方法:
[Route("api/[controller]")]
public class CustomersController : Controller
{
//添加認(rèn)證屬性
[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項目
該項目是用來模擬客戶端訪問資源服務(wù)器整個認(rèn)證流程測試項目,在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;
}
}
運行項目看看測試結(jié)果:

結(jié)合代碼,我們能看到當(dāng)客戶端通過Ocelot網(wǎng)關(guān)訪問下游服務(wù)http://localhost:9000/api/Customers/Get方法時候,因為該方法是需要通過認(rèn)證才返回處理結(jié)果的,所以會進(jìn)行JWT Token認(rèn)證,如果發(fā)現(xiàn)沒有Token,Ocelot則返回http狀態(tài)代碼401拒絕訪問。如果我們通過GetJwt方法在AuthServer服務(wù)上登錄認(rèn)證獲取到授權(quán)Token,然后再訪問該資源服務(wù)器接口,立即就會返回處理結(jié)果,通過跟而未加認(rèn)證屬性的http://localhost:9000/api/Customers/Get/{id}方法對比,我們就知道,Ocelot認(rèn)證已經(jīng)成功了!
4.總結(jié)
該章節(jié)只是結(jié)合demo項目簡單介紹在Ocelot中如何使用JWT Token認(rèn)證。其實正式環(huán)境中,Ocelot是應(yīng)該集成IdentityServer認(rèn)證授權(quán)的,同樣的通過重寫Ocelot中間件我們還可以把configuration.json的配置信息存儲到數(shù)據(jù)庫或者緩存到Redis中。
參考文獻(xiàn):
Ocelot官網(wǎng)
到此這篇關(guān)于ASP.NET Core3.1 Ocelot認(rèn)證的實現(xiàn)的文章就介紹到這了,更多相關(guān)ASP.NET Core3.1 Ocelot認(rèn)證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- ASP.NET Core為Ocelot網(wǎng)關(guān)配置Swagger
- ASP.NET?Core設(shè)置Ocelot網(wǎng)關(guān)限流
- ASP.NET?Core中的Ocelot網(wǎng)關(guān)介紹
- .Net?Core微服務(wù)網(wǎng)關(guān)Ocelot超時、熔斷、限流
- .Net?Core微服務(wù)網(wǎng)關(guān)Ocelot集成Consul
- .Net?Core微服務(wù)網(wǎng)關(guān)Ocelot基礎(chǔ)介紹及集成
- ASP.NET Core Api網(wǎng)關(guān)Ocelot的使用初探
- ASP.NET Core3.1 Ocelot負(fù)載均衡的實現(xiàn)
- ASP.NET Core3.1 Ocelot路由的實現(xiàn)
- Asp.Net?Core使用Ocelot結(jié)合Consul實現(xiàn)服務(wù)注冊和發(fā)現(xiàn)
相關(guān)文章
asp.net顯示相同數(shù)字相乘的結(jié)果,直到數(shù)值大于150為止
老師布置Insus.NET做的第二道題,題目如標(biāo)題。感興趣的網(wǎng)友也可以練習(xí)練習(xí)。現(xiàn)在Insus.NET的作答如下,但老師還沒有看,因此答案是否正確或是最好的,還不能確定,只是供參考2012-05-05
Asp.net 通用萬級數(shù)據(jù)分頁代碼[修正下載地址]
在萬級數(shù)據(jù)量下的分頁代碼2008-10-10
.Net獲取URL中文參數(shù)值的亂碼問題解決方法總結(jié)
這篇文章主要介紹了.Net獲取URL中文參數(shù)值的亂碼問題解決方法,總結(jié)分析了針對URL參數(shù)傳遞中出現(xiàn)的亂碼問題與相應(yīng)的解決方法,具有一定參考借鑒價值,需要的朋友可以參考下2016-08-08
.net 運用二進(jìn)制位運算進(jìn)行數(shù)據(jù)庫權(quán)限管理
.net 運用二進(jìn)制位運算進(jìn)行數(shù)據(jù)庫權(quán)限管理 ,需要的朋友可以參考一下2013-02-02
asp.net實現(xiàn)非常實用的自定義頁面基類(附源碼)
這篇文章主要介紹了asp.net實現(xiàn)非常實用的自定義頁面基類,包含日志處理、控件賦值、異常處理等功能,非常具有實用價值,需要的朋友可以參考下2015-11-11
ASP.Net Core3.0中使用JWT認(rèn)證的實現(xiàn)
這篇文章主要介紹了ASP.Net Core3.0中使用JWT認(rèn)證的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
CheckBox為CheckBoxList實現(xiàn)全選或全取消選擇(js代碼實現(xiàn))
在管理商品后臺是,由于CheckBoxList的選擇太多,用戶需要一個全選或全取消的功能,這樣操作起來會提高效率同時可以減少誤點等,本文將教大家如何實現(xiàn),有需要的朋友可以參考下,望本文對你有所幫助2013-01-01
asp.net MaxLengthValidator 最大長度驗證控件代碼
如果數(shù)據(jù)庫字段為varchar或char類型,ASP.NET控件在可輸入漢字的情況下,MaxLength屬性不能保證在保存到數(shù)據(jù)庫時不發(fā)生截斷錯誤,因此寫了一個最大長度驗證控件,還可用于多行文本框。2009-12-12

