1. 程式人生 > >網頁中的跨域請求 同源策略、跨域解決方案

網頁中的跨域請求 同源策略、跨域解決方案

 

品習知識點

簡單表述幾個概念,詳解@度娘。

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