ASP.NET Core整合現有系統認證
我們現在大多數轉向ASP.NET Core來使用開發的團隊,應該都不是從0開始搭建系統,而是老的業務系統已經在執行,ASP.NET Core用來開發新模組。那麼解決使用者認證的問題,成為我們的第一個攔路虎。
if(Session["user"]==null)
{
Response.redirect("/login.aspx")
}
在ASP.NET Core下,MVC以一個Middleware加入到整個HTTP管道。在此之前還會新增一個Routing的Middleware,注意這裡的意思也就是說 Routing不再和ASP.NET MVC一樣屬於它的一部分。正好相反,在ASP.NET Core裡面是有一個MVCRouteHandler被 Routing Middleware 加載出來處理請求。關於路由這塊我們後面再說。
如果我們要在MVC Middleware執行之請攔截請求只要加一個Middleware在 MVC Middleware或者Routing之前即可。
public void Configure(IApplicationBuilder app){
app.Use(async (context, next) => {
if (context.Request.Headers.ContainsKey("token"))
{
var token =
context.Request.Headers["token"].FirstOrDefault();
if (token == "jessetalk.cn")
{ await next();
}
}
context.Response.StatusCode = 401;
});
app.UseMvc();
}
上面是我們有簡易的方法實現的一個Middleware,被加到了MVC之前。當Request的Headers中沒有一個值為“jessetalk.cn” 以及 name為” token”的項的時候,我們就返回401狀態,並且不執行後面的處理。(不呼叫 next方法)
定製JWTBearer Authentication
ASP.NET Core為我們實現了JWTBearer Authentication,關於 JWTBearer Authentication的實現可以參考另外一篇文章《在ASP.NET Core中使用JWTBearer Authentication》。我們今天要做的就是通過定製JWTBearer Authentication來達到讓它讀取我們自定義的Token並且用我們自己的方式來校驗這個Token。有點時代倒退的感覺是不是?
如果在時間和人員都足夠的情況下,我們是可能直接整體替換成標準的JWT方案,甚至做到SSO。但是架構是沒有止境的,在一定的時間框架下,要做到高效且安全的切換,這不失為一種好辦法。
首先我們需要看一下在JWTBearer中預設獲取的token是在Authorization的頭裡,Bearer空格加上token。而如果有不規範的做法,可能是直接在headers裡面加了一個token,裡面有一個用我們自己的演算法生成的token。
更改token的來源
JWTBearer authentication handler提供的Events中有一個OnMessageReceived委託可以讓我們從另外的地方讀取token。
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o => {
o.Events = new JwtBearerEvents()
{
OnMessageReceived = context => {
var token = context.Request.Headers["token"];
context.Token = token.FirstOrDefault();
return Task.CompletedTask;
},
};
});
定製token的驗證方式
從headers裡面拿到token之後,下一步就是要把它的驗證演算法改成我們自己的。這一步可以通過自定義 ISecurityTokenValidator來實現 。我們在這個Validator裡面,校驗token生成一個ClaimsPrincipal,這個principal就會被賦值到 HttpContext.User上。
同時我們還根據當前的token添加了一個Role Claim,它的值有user和admin。這個可能用來做基於角色的授權 。
public class MyTokenValidator : ISecurityTokenValidator{
public ClaimsPrincipal ValidateToken(string securityToken,
TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
validatedToken = null;
var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
identity.AddClaim(new Claim("name", "jesse"));
identity.AddClaim(new Claim(ClaimsIdentity.DefaultRoleClaimType, securityToken == "jessetalk.cn" ? "admin" : "user")); var principal = new ClaimsPrincipal(identity); return principal;
}
}
注意ClaimsIdentity的AuthenticationScheme一定要與我們在UseAuthentication時設定的名稱一樣。否則Identity.IsAuthenticated無法正確設定為true,我們的授權就沒有辦法完成。
有了我們自定義的Validator之後,我們要對JwtBearer進行改造,去掉它預設的Validator,加上我們自己定義的這個。
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
o.SecurityTokenValidators.Clear();
o.SecurityTokenValidators.Add(new MyTokenValidator());
});
開始進行授權
為了給大家演示上面的功能,我們新建兩個Controller,一個是Admin,另一個是Home。兩者都需要使用者有token才能正常訪問,但是對於Admin我們需要使用者具有admin的role才可以,否則會被拒絕返回403。
HomeController.cs
[Authorize]public class HomeController : Controller{
public IActionResult Index() {
return Ok();
}
}
當Headers裡面沒有token 值的時候,API請求返回 401。
當Headers裡面有token值時,API可以被正常訪問。
我們又加了一個AdminController,不一樣的是這次我們給Authorize加上了Role=”admin”,也就是隻有擁有admin的Role才可以訪問這個API。
[Authorize(Roles ="admin")]
public class AdminController : Controller{
public IActionResult Index() {
return Ok();
}
}
當我們用user的token訪問時,我們會得到403。
只有用admin的token,才能正常訪問。
以是就是基於JWT Authentication來定製的我們自己的認證方案的一個基本思路,主要是實現OnMessageReceived來改造token的來源,以及定義自己的 ISecurityTokenValidator
來實現對token的驗證。
原文地址:http://www.jessetalk.cn/2017/11/03/asp-net-core-authentication-with-legacy-system/
.NET社群新聞,深度好文,歡迎訪問公眾號文章彙總 http://www.csharpkit.com