重新整理 .net core 實踐篇————閘道器中的身份簽名認證[三十七]

敖毛毛發表於2021-07-07

前言

簡單整理一下閘道器中的jwt,jwt用於授權認證的,其實關於認證授權這塊https://www.cnblogs.com/aoximin/p/12268520.html 這個連結的時候就已經寫了,當然只寫到了4節,後面有10節沒有寫,是相對複雜的場景,後續會補齊。

正文

jwt 是json web tokens,是一種支援前面的資料結構。

至於什麼是jwt的話,https://www.jianshu.com/p/576dbf44b2ae 這裡有人寫的比較好了。

然後前文也提及到為什麼在閘道器做身份認證了,因為這樣就不需要專門呼叫另外一個服務來實現身份認證,身份認證授權可以在閘道器和所以微服務同時生效。

教程直接在.net core文件裡面搜尋jwt即可查閱到。

那麼就根據例子要演示一下:

首先加入對應的包:

在appsettings.json 中加入加密金鑰:

"SecurityKey": "asdfghjklqwertyuiopzxcvbnm"

然後注入服務:

var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["SecurityKey"]));
services.AddSingleton(securityKey);
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
	.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
	{

	}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
	{
		options.TokenValidationParameters = new TokenValidationParameters
		{
			ValidateIssuer = true,
			ValidateAudience = true,
			ValidateLifetime = true,
			ClockSkew = TimeSpan.FromSeconds(30),
			ValidateIssuerSigningKey = true,
			ValidAudience = "localhost",
			ValidIssuer = "localhost",
			IssuerSigningKey = securityKey
		};
	});

上面ValidateIssuer 、ValidateAudience 、ValidateLifetime 分別是驗證Issuer、Audience、Lifetime這三個的,其實Issuer、Audience驗證和不驗證的話影響不大,主要是因為jwt隨時可以解密出來,就是經過base64位的轉換。
加入中介軟體:

app.UseAuthentication();

app.UseAuthorization();

測試程式碼:

[HttpGet]
public async Task<IActionResult> JwtLogin([FromServices]SymmetricSecurityKey securityKey,string userName)
{
	List<Claim> claims = new List<Claim>();
	claims.Add(new Claim("Name", userName));
	var creds = new SigningCredentials(securityKey,SecurityAlgorithms.HmacSha256);
	var token = new JwtSecurityToken(
		 issuer:"localhost",
		 audience:"localhost",
		 claims:claims,
		 expires:DateTime.Now.AddMinutes(30),
		 signingCredentials:creds
		);
	var t = new JwtSecurityTokenHandler().WriteToken(token);
	return Content(t);
}

然後呼叫生成jwt:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOYW1lIjoiMTIzIiwiZXhwIjoxNjI1NjE0NDkyLCJpc3MiOiJsb2NhbGhvc3QiLCJhdWQiOiJsb2NhbGhvc3QifQ.lQg70rMofgue9X_RQ1ft_NDmXyY2OJbTZodN4krVOUM

然後通過下面網址進行解密:

https://www.box3.cn/tools/jwt.html

然後來測試一下這個是否生效:

[ApiController]
[Route("[controller]")]
public class OrderController : Controller
{
	[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme+","+CookieAuthenticationDefaults.AuthenticationScheme)]
	public IActionResult oneApi()
	{
		return Content(User.FindFirst("name").Value);
	}
}

上面[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme+","+CookieAuthenticationDefaults.AuthenticationScheme)]的意思是可以通過jwt驗證也可以通過cookie來驗證。

為什麼要這麼寫呢?因為:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
	.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
	{

	}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options ....

上面寫了兩種方式,且AddCookie在前,那麼如果只寫Authorize,那麼預設是cookie,如果AuthenticationSchemes 為JwtBearerDefaults.AuthenticationScheme,那麼只有jwt。

一般我們的專案中用jwt,但是因為有些網站剛開始的時候覺得方便用了cookie,後來因為有app介面,那麼需要新增jwt,這裡不是隻app介面不能用cookie,而是不太方便。

那麼效果如下:

可以看到傳遞了一個Authoization的引數,這個引數是約定引數,那麼值就是Bearer加空格,然後後面接的就是jwt了,這些是約定。

那麼返回了123,這個123就是我們jwt中Claim的key為name的值,呼叫就是User.FindFirst("name").Value,還是相當方便的。

當然我們也可以通過cookie來進行認證:

[HttpGet]
public async Task<IActionResult> CookieLogin(string userName)
{
	var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
	identity.AddClaim(new Claim("Name", userName));
	await this.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,new ClaimsPrincipal(identity));
	return Content("login");
}

這樣把cookie 傳入到響應流中傳遞給前端。
然後訪問:http://localhost:5000/account?username=ao

可以看到有個cookie。

那麼訪問一下剛才的oneApi介面,得到為:

那麼這樣就在閘道器實現了身份簽名和認證了。那麼如果其他服務需要進行認證的話,那麼可以新增相同的認證方式即可。

細節篇介紹一下這個的原始碼。

下一節跨域請求的安全問題。

相關文章