1. 问题详情
- 21点:大晚上的接到一个电话.说生产服务器无法接受支付完成的notify,让我帮忙看看咋回事.
- 我说: 前不久,用支付宝扫二维码支付还提示商家信息有问题.估计是支付平台的问题,联系一下他们.
- 21点15分:支付平台没有回复.但是经测试发现用get请求接口是可以的.post不行.
- 我说: ????? 这么神奇 ?????
- 使用post请求其他接口,也是直接500.
- 我说: ????? 这么神奇 ?????
2. 解决
刚开始完全没有头绪?什么神奇的bug?nginx还有这个神奇的功能?
2.1. 思路
委托:
- 云平台:阿里,应该没问题
- 主要是检查安全组策略
- Web服务器:Nginx,也没问题.
- 检查
$request_method
: 发现没有配置类似的转发或deny - 检查虚拟主机: 也没啥问题
- 检查
从代码入手:把我加入了组织,发现用的YII2.猜测就是CSRF配置的问题.但是对方坚决否认说.CSRF全部关闭了.
-
修改index.php:
- 加入一句话打印
$_POST
- 用postman请求
- 成功
- 说明是框架内部的问题
- 加入一句话打印
-
为了证明我的观点:委托对方做了如下修改
- 修改运行环境:index.php
- 第一行debug改成true
- 第二行prod改为dev
- 修改PHP配置,输出log
-
php.ini:
log_errors = On error_log = "/usr/local/php/var/log/error_log" error_reporting=E_ALL&~E_NOTICE
-
php-fpm.conf
[global] ; Note: the default prefix is /usr/local/php/var error_log = "/usr/local/php/var/log/error_log" ; php_admin_value[error_log]这个会覆盖php.ini中的error_log配置,不要设置 ; 这个只是php-fpm的错误信息
-
对应的pool
[www] catch_workers_output = yes ; 上面的配置是抓取对应的pool的错误,模式是把他们直接输入到/dev/null
-
- 修改运行环境:index.php
-
测试:log中果然是CSRF的问题.
2.2. 解决
发现:他们开启了CSRF,然后支付平台的通知使用的是POST.header中没有,所以在beforeAction中就被拒绝了,而且他们用的生产环境的配置.不显示错误.直接500.就让人感觉很奇怪.
2.2.1. 整体关闭CSRF
main-local.php的配置中:
//加个字段
request => [
'enableCookieValidation' => true,
]
2.2.2. 控制器关闭CSRF
public $enableCsrfValidation = false;
2.2.3. 某一个Action关闭CSRF
//控制器中重写一下beforeAction
public function beforeAction($action) {
$currentAction_Str = $action->id;
$csrfOffActions_Arr = ['notify'];
if(in_array($currentAction_Str,$csrfOffActions_Arr)) {
$action->controller->enableCsrfValidation = false;
}
return parent::beforeAction($action);
}
2.3. 还有一个问题
关于访问控制
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['goodsList', 'goodsDetail', 'cart', 'addCart', 'logout', 'delCart', 'payment', 'addOrder', 'subOrder', 'apply', 'notify', 'success'],
'rules' => [
[
'actions' => ['goodsList', 'goodsDetail', 'addCart', 'notify', 'success'],
'allow' => true,
'roles' => ['?', '@'],//不管是否登录都可以访问的页面,最好写成这样.
],
[
'actions' => ['logout', 'cart', 'delCart', 'payment', 'addOrder', 'subOrder', 'apply'],
'allow' => true,
'roles' => ['@'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
//默认是所有访问方式都可以.
'logout' => ['post'],//可以指定logout使用post
// 'notify'=>['post'],
],
],
];
}