相信“跨域资源共享”(Cross-origin resource sharing)对于论坛内的大部分人来说,已经是老生常谈了,所以就不再赘述关于CORS的定义、各种解决方案等等,不过可以推荐阮大神的一篇相关博文,希望从头了解CORS的可以参考。这里主要写下关于混合开发时针对各个端(web、Android、iOS、微信小程序)的跨域问题的处理。
如果你是一个对站点安全比较佛系,追求可用性的人,那直接
header('Access-Control-Allow-Origin:*');
就可以把“跨域”二字抛之脑后,大门敞开,来者不拒,也就不会担心自己人因跨域被拦截了。当然,对于公开运营的站点,最好别这么做。
若是希望自己的家不被陌生人随意进入,导致家里出现“意外”,那这个 Access-Control-Allow-Origin就需要按需设置了。
由于我们需要设置让指定的域名允许跨域访问,那么就设置一个白名单$allowedOrigins,每次判断来访的域名是否在白名单中,是的话就设置接口响应的header中Access-Control-Allow-Origin的值为该域名。具体代码可以参考下面。
const ALLOW_ORIGINS = [
'https://foo.com',
'https://bar.com'
];
private function checkOrigin()
{
if (isset($_SERVER["HTTP_ORIGIN"])) {
$httpOrigin = $_SERVER["HTTP_ORIGIN"];
in_array($httpOrigin, self::ALLOW_ORIGINS) && header('Access-Control-Allow-Origin:' . $httpOrigin);
}
}
以上代码已经可以解决web端的跨域问题,但是因为跨域本身就是客户端浏览器的控制,如果是混合开发,还需要考虑安卓端跟ios端app的问题。由于我们是采用uniapp进行跨平台开发,针对安卓端应用,包括微信小程序,目前没发现跨域问题,但是ios端在配置了上面的代码之后,就出现了跨域问题,排查后发现ios端发送到服务端接口的HTTP_ORIGIN值是字符串"null”,查询之后发现,ios端原生的APP发送请求时,HTTP_ORIGIN一般为localhost:8000 ,但我们这个是经过封装的,uniapp使用cordova包进行底层封装处理,使用这个包时api请求发送时会带值为"null”的HTTP_ORIGIN头。所以为了兼容iOS这种情况,要么就是在ALLOW_ORIGINS中添加一个“null”的域名,要么就是在下面程序中做特殊判断。当然因为本身对手机APP原生开发基本不了解,所以这块原理性的知识欠缺,很难去达到完全的“所以然”的程度,如果有APP原生开发的小伙伴对这块有研究过,十分欢迎指教。
另外补充个“题外话”,上面代码中用到了in_array(),如果ALLOW_ORIGINS数据量很大的话,最好能先把ALLOW_ORIGINS改成以域名为键,然后随意定一个值作为键的值的键值对形式,然后改成用 isset(self::ALLOW_ORIGINS[$httpOrigin])这样来判断,因为in_array的时间复杂度为O(n),在n很大的时候,还是有优化空间的。同时isset属于php的语法结构,像in_array(),array_key_exists()属于函数,相比之下isset还是效率略高的。
本作品采用《CC 协议》,转载必须注明作者和本文链接