当前位置: 移动技术网 > IT编程>开发语言>.net > .Net Core Cors中间件的深入讲解

.Net Core Cors中间件的深入讲解

2018年08月08日  | 移动技术网IT编程  | 我要评论

好看电影网站,汉中电视台直播,嘻哈包袱铺成员

同源策略和资源跨域共享

1、同源策略

同源策略,它是由netscape提出的一个著名的安全策略。现在所有支持javascript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。

1.1、目的

主要是为了保证用户信息的安全,防止网站窃取用户数据。假如没有同源策略,可能就会有下面这种情况的发生。用户访问两个网站a/b,并登录了a网站,a网站会在计算机本地存储cookie或者token等等,在访问b网站的时候,b网站就可以访问这些本地的存储信息,b网站可以使用用户的cookie去登录a网站,那这样用户信息就被泄露了。

1.2、限制范围   

  • cookie、localstorage和indexdb无法访问(只有同源的网页才能共享cookie)
  • dom无法获得(父窗口和子窗口的地址是同源的才能获取子窗口的信息)
  • ajax请求不能被发送(ajax请求只能发送给同源的网址)

要知道一点,这些限制其实都是浏览器做的限制。

2、跨域资源共享

跨域资源共享跟同源策略相反。在整个跨域通信过程中,浏览器会自动识别此次请求是否跨域,一旦发现跨域,就自动添加请求头信息(如origin)或者自动发送一次请求方式为option的预请求。浏览器将cors请求分为两类:简单请求和非简单请求。

2.1、简单请求

当浏览器的请求方式是head、get或者post,并且http的头信息中不会超出以下字段:

  • accept
  • accept-language
  • content-language
  • origin

时,浏览器会将该请求定义为简单请求,否则就是非简单请求。当浏览器判断为简单请求后,浏览器会自动再请求报文头中加上origin字段,表明此次请求来自的地址(协议+域名+端口)。然后服务器需要去判断是否接受这个来源的请求。如果允许服务器端返回的头部中需要有access-control-allow-origin,其值为请求时origin字段的值或*(表示接受任意源的请求)。请求头中还会有access-control-allow-methods表示服务器允许的跨域请求的方式。access-control-allow-headers表示请求头中允许出现的字段。

2.2、 非简单请求

当浏览器判断为非简单请求后,会发送两次请求,首先浏览器会自动发送一个请求方式为options的请求,并在请求头中

  • 加上access-control-request-method表示下次请求的方法,
  • 加上origin表明来源,
  • 加上access-control-request-headers表示下次请求的请求头中额外的字段。

服务器收到请求后,需要获取这三个请求头中的值,并进行判断,确认是否允许进行跨域。如果服务器返回的请求头中没有任何cors相关的请求头信息,浏览器会认为不通过预检,也不会进行第二次请求。

服务器如果接受跨域并验证通过了options的请求,会返回access-control-allow-origin(表明允许跨域请求的源)、access-control-allow-methods(允许跨域请求的请求方式)、access-control-allow-headers(允许请求头中包含的额外字段)。然后浏览器才会发送真正的请求。 

                 

(第一次options请求)

(第二次请求)

二、服务端实现cors

在.net core web api中使用很简单,首先安装包microsoft.aspnet.webapi.cors,在startup中添加下面两句

public void configureservices(iservicecollection services)
 {
  services.addmvc();
       //添加cors,并配置corspolicy 
  services.addcors(options => options.addpolicy("corstest", p => p.allowanyorigin().allowanyheader().allowanymethod()));
 }

 public void configure(iapplicationbuilder app, ihostingenvironment env, iloggerfactory loggerfactory)
 {
  if (env.isdevelopment())
  {
  app.usedeveloperexceptionpage();
  }       //注意usecors()要在usemvc()之前
  app.usecors("corstest");
  app.usemvc();
 }

在使用的时候只需要在controller或者action中加上特性[enablecors("corstest")]

[enablecors("corstest")]
 public class valuescontroller : controller
 {
 private ilogger<valuescontroller> _logger;
 public valuescontroller(ilogger<valuescontroller> logger)
 {
  _logger = logger;
 }
 [httpget]
 public ienumerable<string> get()
 {
  return new string[] { "value1", "value2" };
 }
 }

现在服务端已经配置好了,现在需要通过前端跨域请求

<html>
<head>
 测试
</head>
<body>
 测试
</body>
</html>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
 $(function () {
 $.ajax({
  type: "get",
  url: "http://localhost:7000/api/values",
  beforesend: function (request) {//在请求报文头中加入authorization 目的是让请求为非简单请求
  request.setrequestheader("authorization", "bearer 071899a00d4d4c5b1c41a6b0211b9399");
  },
  success: function (result) {
  alert(result);
  }
 }, "json");
 });
</script>

测试结果如下图:

                          (options请求)

                        (第二次请求)

 上面配置允许所有的地址请求这个接口,也可以单独配置某个地址。

services.addcors(options => options.addpolicy("corstest", p => p.withorigins("http://localhost:8089")
                   .allowanyheader()
                   .allowanymethod()));

 三、解析cors源码

打开cors源码,主要的是corsmiddleware、corsoptions、corspolicy、corspolicybuilder、corsresult、corsservice这几个类。

  • corspolicy:就是我们在startup中的配置,如允许哪些域名可以跨域请求,允许哪些跨域请求方式,允许哪些额外的请求头,每个配置对应一个名称。
           services.addcors(options => options.addpolicy("corstest", p => p.allowanyorigin().allowanyheader().allowanymethod()));
  • corsoptions:中包含一个字典idictionary<string, corspolicy> policymap,一个项目可能有过个cors配置,所以这个corsoptions就是通过配置名称管理这些配置的。
  • corspolicybuilder:通过它来构造corspolicy。
  • corsresult:是验证跨域过程得到的结果。如在第一次options请求时,客户端发送了origi:http://localhost:8089,服务器会返回access-control-allow-origin:http://localhost:8089,服务器验证http://localhost:8089这个域名是否允许跨域,如果允许就将“http://localhost:8089”这个值存储到corsresult的allowedheaders中,在请求(第一次请求)返回的时候将这些加到http请求头中。
  • corsmiddleware:cors中间件类,主要方法就是invoke,每次http请求都会调用这个方法。
public async task invoke(httpcontext context)
  {//判断http请求头是否有origin,由此判断是不是跨域请求
   if (context.request.headers.containskey(corsconstants.origin))
   {
    var corspolicy = _policy ?? await _corspolicyprovider?.getpolicyasync(context, _corspolicyname);
    if (corspolicy != null)
    {
     var accesscontrolrequestmethod = context.request.headers[corsconstants.accesscontrolrequestmethod];            //如果是跨域请求 判断是不是第一次options请求
     if (string.equals(context.request.method,corsconstants.preflighthttpmethod,stringcomparison.ordinalignorecase) 
      &&!stringvalues.isnullorempty(accesscontrolrequestmethod))
     {               //判断是否允许当前请求跨域,根据httpcontext的内容和cors配置 得到corsresult,然后将corsresult的内容添加到请求头中(看下面详细解释)
      applycorsheaders(context, corspolicy);
      context.response.statuscode = statuscodes.status204nocontent;
      return;
     }
     else
     {// 执行第二次非options请求
      context.response.onstarting(state =>
      {
       var (httpcontext, policy) = (tuple<httpcontext, corspolicy>)state;
       try
       {
        applycorsheaders(httpcontext, policy);
       }
       catch (exception exception)
       {
        _logger.failedtosetcorsheaders(exception);
       }
       return task.completedtask;
      }, tuple.create(context, corspolicy));
     }
    }
   }
   await _next(context);
  }
    
  private void applycorsheaders(httpcontext context, corspolicy corspolicy)
  {  //通过http上下文请求的数据和cors配置 得到corsresult        如在第一次options请求时,客户端发送了origi:http://localhost:8089,access-control-resquest-methods:get        服务器会返回access-control-allow-origin:http://localhost:8089,access-control-allow-methods:get        服务器验证http://localhost:8089这个域名以get请求方式是否允许跨域,        如果允许就将“http://localhost:8089”这个值存储到corsresult的allowedheaders中        将"get"存储到corsresult的allowedmethods中
   var corsresult = _corsservice.evaluatepolicy(context, corspolicy);        //将corsresult中的值添加到相应头中的,返回到客户端
   _corsservice.applyresult(corsresult, context.response);
  }

相对来说cors源码还是比较简单的,很容易看懂。可以自己写一个项目,然后挂上源码单步调试。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对移动技术网的支持。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网