我们现在大多数转向ASP.NET Core来使用开发的团队,应该都不是从0开始搭建系统,而是老的业务系统已经在运行,ASP.NET Core用来开发新模块。那么解决用户认证的问题,成为我们的第一个拦路虎。
认证与授权
if(Session["user"]==null)
{Response.redirect("/login.aspx")
}
用Middleware拦截
在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