zoukankan      html  css  js  c++  java
  • 网页中的跨域请求

    品习知识点

    简单表述几个概念,详解@度娘。

    1、同源策略,浏览器最核心的安全功能,在无授权情况下,只允许读写相同源的资源。其中源(Origin)指的是协议、域名、接口,同源即三者相同。

    2、预检请求,浏览器出于安全策略,在跨域请求数据时候预先发起请求,以知是否可跨域请求数据的请求。

    关于以上两个知识点,推荐参考以下几篇文章,写得很好。

    CORS简介

    同源策略、跨域解决方案

    Ajax跨域、Json跨域、Socket跨域和Canvas跨域等同源策略限制的解决方法

    预检请求(preflight request)

    浅谈关于预检请求


    针对跨域请求的问题,会有很多种解决办法。下面,我提供个人用的两种办法。

    第一种解决方案,是非常简单的

    第一次遇到跨域的问题,我用了非常简便的方法:在 Web.config 配置文件中添加配置。配置如下,

        <!--允许跨域 开始-->  
        <httpProtocol>
          <customHeaders>
            <add name="Access-Control-Allow-Origin" value="http://domain.testweb.cn:18833" />
            <add name="Access-Control-Allow-Credentials" value="true" />
            <add name="Access-Control-Allow-Headers" value="Content-Type" />
            <add name="Content-Security-Policy" value="upgrade-insecure-requests" />
            <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
          </customHeaders>
        </httpProtocol>
        <!--允许跨域 结束-->

    在节点 <customHeaders> 中配置可跨域请求的来源:<add name="Access-Control-Allow-Origin" value="http://domain.testweb.cn:18833" />

    如此一来,以上配置的源请求便可在浏览器中直接访问该站点的资源。

    这个解决办法,简洁明了,美观大方,直接暴力,方便有效。

    但,也有个弊端,Access-Control-Allow-Origin 的值只能设置一个源 or 任何源(设置 * 号值表示任何源),设置 * 号则对所有请求一视同仁了。


    第二种解决方案,是相当灵活的

    第二次遇到跨域的问题,业务要求就比第一次的复杂一些。要求针对特定的一些源以及部分被访问的数据接口设置可跨域请求。

    以上第一种解决跨域的方案满足不了这样的需求。我们用到了以下这种解决办法。

    在 Global.asax.cs 中拦截到请求,对访问的请求作判断和处理。

            #region 请求响应处理
            /// <summary>
            /// 请求响应处理
            /// </summary>
            protected void Application_BeginRequest()
            {
                List<IfCrossDomainAccess> infCrossAccess = KeysHelper.GetIfCrossDomainAccess();
                if (infCrossAccess != null && infCrossAccess.Count > 0)
                {
                    string requestInf = Request.Url.AbsolutePath.ToLower();
                    string requestOrigin = Request.Headers["Origin"] == null ? string.Empty : Request.Headers["Origin"].ToString().ToLower();
                    List<string> inf = infCrossAccess.Select(p => p.Interface).ToList();
                    if (inf.Any(p => !string.IsNullOrWhiteSpace(p) && p == requestInf)) //接口有允许跨域
                    {
                        IfCrossDomainAccess inft = infCrossAccess.FirstOrDefault(p => p.Interface == requestInf);
                        if (inft != null && inft.CrossDomain != null && inft.CrossDomain.Count > 0)
                        {
                            CrossDomain domain = inft.CrossDomain.FirstOrDefault(pp => pp.Domain == requestOrigin); //请求来源域名允许访问
                            if (domain != null && !string.IsNullOrWhiteSpace(domain.Method))
                            {
                                Response.Headers.Add("Access-Control-Allow-Origin", domain.Domain);
                                Response.Headers.Add("Access-Control-Allow-Credentials", "true");
                                Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
                                Response.Headers.Add("Content-Security-Policy", "upgrade-insecure-requests");
                                Response.Headers.Add("Access-Control-Allow-Methods", domain.Method);
                            }
                        }
                    }
                }
                if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS") Response.End(); //Preflighted Requests(预检请求) 处理
            }
            #endregion

    获取跨域配置

            #region 获取允许跨域访问的接口
            /// <summary>
            /// 获取允许跨域访问的接口
            /// </summary>
            /// <returns></returns>
            public static List<IfCrossDomainAccess> GetIfCrossDomainAccess()
            {
                List<IfCrossDomainAccess> inf = new List<IfCrossDomainAccess>();
                try
                {
                    IfCrossDomainAccess infCross = new IfCrossDomainAccess();
                    if (!File.Exists(CurrentConfig)) return inf;
                    XmlDocument xml = new XmlDocument();
                    xml.Load(CurrentConfig);
                    XmlNode xNode = xml.SelectSingleNode("//accessAllowSettings");
                    if (xNode == null) return inf;
                    XmlNodeList infNodes = xNode.SelectNodes("//interface");
                    if (infNodes != null && infNodes.Count > 0)
                    {
                        foreach (XmlNode n in infNodes)
                        {
                            infCross = new IfCrossDomainAccess();
                            infCross.CrossDomain = new List<CrossDomain>();
                            infCross.Interface = n.Attributes["value"].Value.ToLower();
                            XmlNodeList origin = n.ChildNodes;
                            if (origin != null && origin.Count > 0)
                            {
                                foreach (XmlNode o in origin)
                                {
                                    infCross.CrossDomain.Add(new CrossDomain
                                    {
                                        Domain = o.Attributes["value"].Value.ToLower(),
                                        Method = o.Attributes["method"].Value
                                    });
                                }
                            }
                            inf.Add(infCross);
                        }
                    }
                }
                catch (Exception ex){
                    LogHelper.Error("获取允许跨域接口【KeysHelper.GetIfCrossDomainAccess】出错:" + ex);
                    return inf;
                }
                return inf;
            }
            #endregion
    View Code

    跨域配置文件:

    代码逻辑思路:

    1、获取到配置的 允许跨域访问的源和接口;

    2、获取请求头部中的源和请求接口;

    3、匹配检查所请求的接口是否有允许跨域,若有,则再检查请求的来源是否允许跨域访问,若有,则在响应头部中,添加一下属性,

    Access-Control-Allow-Origin,Access-Control-Allow-Credentials,Access-Control-Allow-Headers,Content-Security-Policy,Access-Control-Allow-Methods,

    4、判断当前请求是否是预检请求,若是,停止请求接口数据,将请求的响应返回。

    以上步骤,步骤1 是重要的一部分,可设计动态配置可跨域访问。步骤4 涉及到预检请求的处理,尤为关键。


    跨域请求的信息截图

    跨域请求无跨域允许,浏览器的报错信息:

    跨域请求有得到允许,访问的情况:

     


    author:韦小明

    本文路径:http://www.cnblogs.com/youler/p/9815736.html


  • 相关阅读:
    动态横向(水平)合并Repeater数据行DataItem的列
    动态绑数据(Repeater控件HeaderTemplate和ItemTemplate)
    动态横向(水平)合并GridView数据行DataRow的列
    动态绑数据(GridView控件Header和ItemTemplate)
    用具体列名替代星号
    如何实现数据行转换列显示
    用LINQ获取XML节点数据
    从字符串中获取XML节点数据
    字符串创建XML文档
    根据Attribute值条件对XML文档进行修改
  • 原文地址:https://www.cnblogs.com/youler/p/9815736.html
Copyright © 2011-2022 走看看