ASP.NET Core集成现有系统认证

我们现在大多数转向ASP.NET Core来使用开发的团队,应该都不是从0开始搭建系统,而是老的业务系统已经在运行,ASP.NET Core用来开发新模块。那么解决用户认证的问题,成为我们的第一个拦路虎。本文将给大家简单阐述一下认证与授权的基本概念,以及基于ASP.NET Core 中间件实现的认证和改造JwtBearer 认证中间件来实现的认证达到与老系统(主要是token-based认证)的集成。

目录

  • 认证与授权
    • 什么是认证
    • 何谓授权
    • 用Middleware拦截
  • 定制JWT Bearer 认证
    • 更改token来源
    • 更改token验证方式
    • 开始授权 

认证与授权 

什么是认证? 

首先认证并不是登录。认证是一个知道用户是谁的一个过程。我们最早使用的基于Session的认证,拿到用户输入的用户名和密码到数据库里面校验一,看看是否正确,如果是正确的我们就放到session里面。这是一个完成认证的过程,系统现在知道你是我的某一个用户了。

那么何谓授权? 

现在用户登录之后我们跳转到了另一个页面,这个页面可能会写一段这样的代码。

if(Session["user"]==null)
{
  Response.redirect("/login.aspx")
}

如果用户登录的Session不存在则再跳回到登录页面让用户登录。 检查当前用户有没有某个权限的这个过程叫授权。如果没有怎么办?我们就会跳转用户到一个没有权限的提示页面,或者返回  Forbidden 403 的HTTP 状态码,这是最简单的授权。

复杂的授权方式包括对角色,对具体资源访问以及操作的授权,这块我们后面再讲。

当我们的ASP.NET Core项目需要与老的项目兼容的时候,就需要兼容老项目的认证方式,比如某种自定义的token(这是之前比较常见的做法)。我们需要在ASP.NET Core中根据当前用户header里面的token来判断是否为一个合法的用户。

用Middleware拦截

第一种简单粗暴的方法即用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方法)

但是这种办法相当于一刀切,我们添加的这个Middleware发生在 MVC Middleware之前把所有没有认证信息的请求全部拦截掉了。好处是有节省服务器资源(如果确定是要拦截的就没有必须再经过MVC的一些处理了),坏处是无法实现单个Controller或者Action的灵活配置。

定制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的验证。

本文首发于公众号jessetalk。 如需转载,请保留公众号二维码,谢谢。

更多精彩文章:

ASP.NET Core依赖注入全知道: https://mp.weixin.qq.com/s/lR9O7bXiI704kSu7bKdLGg

我心中的ASP.NET Core新核心对象之WebHost(一) https://mp.weixin.qq.com/s/4Sm2dxMe_WeVOizhqX4ZdA

极简版ASP .NET Core学习路径  https://mp.weixin.qq.com/s/7oKnYLOrff_FmMLm7thnBg

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏pangguoming

C#开发微信公众平台-就这么简单(附Demo)

  最近公司在做微信开发,其实就是接口开发,网上找了很多资料,当然园友也写了很多教程,但都是理论说了一大堆,实用指导或代码很少。如果你自己仔细研究下,其实就那么...

611
来自专栏葡萄城控件技术团队

七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC

? 系列文章 七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC 七天学会ASP.NET MVC (二)——ASP.NET MVC 数据...

2405
来自专栏程序猿DD

Spring Cloud构建微服务架构:分布式服务跟踪(跟踪原理)

通过上一篇《分布式服务跟踪(入门)》的例子,我们已经通过Spring Cloud Sleuth往微服务应用中添加了实现分布式跟踪具备的基本要素。下面通过本文来详...

3085
来自专栏企鹅号快讯

HttpClient使用详解

Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们再讨论),它不...

2675
来自专栏编程

Python多线程(四)

如何使用线程本地数据 实际案例: 实现了一个web视频监控服务器,服务端采集摄像头数据,客户端使用浏览器通过http请求接收数据,服务器使用推送的方式(mult...

1809
来自专栏aoho求索

认证鉴权与API权限控制在微服务架构中的设计与实现(三)

引言: 本文系《认证鉴权与API权限控制在微服务架构中的设计与实现》系列的第三篇,本文重点讲解token以及API级别的鉴权。本文对涉及到的大部分代码进行了分析...

5214
来自专栏iKcamp

走进Node.js 之 HTTP实现分析

作者:正龙(沪江Web前端开发工程师) 本文为原创文章,转载请注明作者及出处 上文“走进Node.js启动过程”中我们算是成功入门了。既然Node.js的强...

2536
来自专栏java学习

JavaEE就业学习路线(给初学者以及自学者一个学习方向)

第一节java入门 1-Java 背景介绍 2-Java 入门程序的编写 3-环境配置 4-基本概念介绍 5-类型转换 6-开发工具使用 第二节java基础 1...

2617
来自专栏JetpropelledSnake

RESTful源码学习笔记之RPC和Restful深入理解

RPC 即远程过程调用(Remote Procedure Call Protocol,简称RPC),像调用本地服务(方法)一样调用服务器的服务(方法)。通常的实...

653
来自专栏小勇DW3

网页爬虫小记:两种方式的爬取网站内容

此处进行简单的分类,对于普通的网页爬取内容,如果没有登录界面可以直接使用Jsoup的API进行爬取;

732

扫码关注云+社区