品习知识点
简单表述几个概念,详解@度娘。
1、同源策略,浏览器最核心的安全功能,在无授权情况下,只允许读写相同源的资源。其中源(Origin)指的是协议、域名、接口,同源即三者相同。
2、预检请求,浏览器出于安全策略,在跨域请求数据时候预先发起请求,以知是否可跨域请求数据的请求。
关于以上两个知识点,推荐参考以下几篇文章,写得很好。
Ajax跨域、Json跨域、Socket跨域和Canvas跨域等同源策略限制的解决方法
针对跨域请求的问题,会有很多种解决办法。下面,我提供个人用的两种办法。
第一种解决方案,是非常简单的
第一次遇到跨域的问题,我用了非常简便的方法:在 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
跨域配置文件:
代码逻辑思路:
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