阿猫的博客

阿猫的博客

thinkphp5.0 全局跨域体验最佳解决方案

486
2021-03-28

跨域

出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。

简单来说:在前后端分离开发的时候,由于前端使用一些框架等,会出现跨域的问题。

更多跨域相关可以参考: Web跨域请求及其解决方案 什么是跨域?跨域解决方法

经典解决方法

一般而言,只需要加入以下三行代码即可解决

//指定允许其他域名访问header('Access-Control-Allow-Origin:*');//响应类型header('Access-Control-Allow-Methods:*');//响应头设置header('Access-Control-Allow-Headers:x-requested-with,content-type');

但是这个方法在thinkphp框架中使用时,如果有多个控制器,或控制器下的方法比较多,又不能明确确定哪些需要跨域哪些不需要的情况下,逐个方法添加这三行代码,显得非常僵硬。

workaround:代码上的优化 当然也有利用一些小技巧可以让这三行代码只出现一次的方法,例如直接在文件头放这三行代码(无法解决多个控制器的问题)、设置一个BaseController在这个基类中放这三行代码(结构不太规范)。

更优雅的方法

太长不看

./application/index/tags.php中找到app_init,并注册解决跨域的类

<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 <http://thinkphp.cn> All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( <http://www.apache.org/licenses/LICENSE-2.0> )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------

// 应用行为扩展定义文件
return [
    // 应用初始化
    'app_init'     => ['app\\index\\behavior\\CORS'],
    // 应用开始
    'app_begin'    => [],
    // 模块初始化
    'module_init'  => [],
    // 操作开始执行
    'action_begin' => [],
    // 视图内容过滤
    'view_filter'  => [],
    // 日志写入
    'log_write'    => [],
    // 应用结束
    'app_end'      => [],
];

./application/index/behavior/中新建一个cors.php,将我们要拦截并添加的三行代码放上去

<?php

namespace app\\index\\behavior;

use think\\Response;

class CORS
{
    public function appInit(){
        header('Access-Control-Allow-Origin:*');
        header('Access-Control-Allow-Methods:*');
        header('Access-Control-Allow-Headers:x-requested-with,content-type');
    }
}

使用postman测试,发现有如下三行headers即为成功。

https://img-blog.csdnimg.cn/20200212220458549.png

跨域问题解决的原理

观察上面的三行代码可以发现,只需要在response中加入这三个header就可以了。一般情况下,只要有合适的认证机制(如token)就可以有效防止CSRF攻击,因此可以直接设置一个全局的跨域。

思路

在这篇文章(搞定thinkPHP5.0 跨域问题)发现了tp5原来还有叫做行为、有点类似spring中AOP的机制,又有点像FIlter(可以用Filter解决utf8编码的问题!跟这个有点相似)。

关于行为的描述 ThinkPHP5.0完全开发手册-行为

另外

在面向百度编程的时候发现跨域问题在tp5.1中已经有官方的实现了,而且可以根据路由或分组进行跨域请求支持(ThinkPHP5.1完全开发手册-路由-跨域请求)。还有另外一个据说可以在tp5.0中使用的轮子(Github-medz/cors),虽然但是我没有成功使用,再虽然但是这个实现比起上述方法臃肿麻烦,在简单项目中大可不必。

再另外,上述用token就能防范CSRF攻击的说法不一定准确,直接全局跨域肯定会存在一定的风险。如果对安全方面有较高考虑建议使用tp5.1的分路由跨域。