Yii2 REST+ Angular Междоменный CORS

Я разработал сервис Angular и Yii2 REST. Есть проблема в междоменном. Здесь ниже добавьте мой код angular и Yii2 REST.

AngularJs: (например, 'http://organization1.example.com','http://organization2.example.com',....)

$http.defaults.useXDomain = true;
$http.defaults.withCredentials = true;
$http.defaults.headers.common['Authorization'] = 'Bearer ' + MYTOKEN

Мой запрос от Angular Controller:

apiURL = 'http://api.example.com';
$http.get(apiURL + '/roles')
     .success(function (roles) { })
     .error(function () { });

Yii2 .htaccess: (URL REST типа 'http://api.example.com')

Header always set Access-Control-Allow-Origin: "*"
Header always set Access-Control-Allow-Credentials: true
Header always set Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
Header always set Access-Control-Allow-Headers "Authorization,X-Requested-With, content-type"

Yii2 Мое поведение:

public function behaviors() {
    $behaviors = parent::behaviors();
    $behaviors['corsFilter'] = [
        'class' => Cors::className(),
        'cors' => [
            'Origin' => ['*'],
            'Access-Control-Expose-Headers' => [
                'X-Pagination-Per-Page',
                'X-Pagination-Total-Count',
                'X-Pagination-Current-Page',
                'X-Pagination-Page-Count',
            ],
        ],
    ];
    $behaviors['authenticator'] = [
        'class' => HttpBearerAuth::className(),
        'except' => ['options'],
    ];
    $behaviors['contentNegotiator'] = [
        'class' => ContentNegotiator::className(),
        'formats' => [
            'application/json' => Response::FORMAT_JSON,
        ],
    ];

    return $behaviors;
}

Проблема

Из моего углового запроса есть метод «GET», но он будет использовать метод «OPTIONS» и вернуть 401 Несанкционированная ошибка (CORS). потому что заголовок авторизации запроса не отправляется.


person ArivuAjay    schedule 12.02.2016    source источник


Ответы (3)


Обновление:

Как указал @jlapoutre, теперь это хорошо описано в официальных документах< /а>:

Добавление фильтра Cross-Origin Resource Sharing к контроллеру немного сложнее, чем добавление других фильтров, описанных выше, поскольку фильтр CORS должен применяться до методов проверки подлинности и, следовательно, требует несколько иного подхода по сравнению с другими фильтрами. Кроме того, аутентификация должна быть отключена для предварительных запросов CORS, чтобы браузер мог безопасно определить, можно ли сделать запрос заранее без необходимости отправки учетных данных аутентификации. Ниже показан код, необходимый для добавления фильтра yii\filters\Cors к существующему контроллеру, являющемуся продолжением yii\rest\ActiveController:

use yii\filters\auth\HttpBasicAuth;

public function behaviors()
{
    $behaviors = parent::behaviors();

    // remove authentication filter
    $auth = $behaviors['authenticator'];
    unset($behaviors['authenticator']);

    // add CORS filter
    $behaviors['corsFilter'] = [
        'class' => \yii\filters\Cors::className(),
    ];

    // re-add authentication filter
    $behaviors['authenticator'] = $auth;
    // avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method)
    $behaviors['authenticator']['except'] = ['options'];

    return $behaviors;
}

Старый ответ (устаревший)

При слиянии с parent::behaviors() возникает проблема с порядком. Полная информация здесь.

Я бы рекомендовал не определять ключи при слиянии с родительским массивом:

public function behaviors()
{
    return \yii\helpers\ArrayHelper::merge([
        [
            'class' => \yii\filters\Cors::className(),
            'cors' => [...],
        ],
        [
            'class' => \yii\filters\auth\HttpBearerAuth::className(),
            'except' => ['options'],
        ],
        [
            'class' => ContentNegotiator::className(),
            'formats' => [...],
        ]
    ], parent::behaviors());
}
person Salem Ouerdani    schedule 12.03.2016
comment
Теперь это четко задокументировано здесь: yiiframework.com/doc-2.0 /guide-rest-controllers.html#cors — просто следуйте этому руководству, и оно сработает. Резюме: убедитесь, что аутентификация выполняется после поведения CORS, и всегда исключайте OPTIONS из аутентификации. - person jlapoutre; 26.08.2016

В вашем контроллере:

use yii\filters\Cors;
...
public function behaviors()
{
    return array_merge([
        'cors' => [
            'class' => Cors::className(),
            #special rules for particular action
            'actions' => [
                'your-action-name' => [
                    #web-servers which you alllow cross-domain access
                    'Origin' => ['*'],
                    'Access-Control-Request-Method' => ['POST'],
                    'Access-Control-Request-Headers' => ['*'],
                    'Access-Control-Allow-Credentials' => null,
                    'Access-Control-Max-Age' => 86400,
                    'Access-Control-Expose-Headers' => [],
                ]
            ],
            #common rules
            'cors' => [
                'Origin' => [],
                'Access-Control-Request-Method' => [],
                'Access-Control-Request-Headers' => [],
                'Access-Control-Allow-Credentials' => null,
                'Access-Control-Max-Age' => 0,
                'Access-Control-Expose-Headers' => [],
            ]
        ],
    ], parent::behaviors());
}

Документация

person Sergey Krivochenko    schedule 12.02.2016

проблема с вашим кодом, вы не отключаете аутентификацию в самом начале

public function behaviors() {
$behaviors = parent::behaviors();
/*unset here*/
unset($behaviors['authenticator']);
$behaviors['corsFilter'] = [
    'class' => Cors::className(),
    'cors' => [
        'Origin' => ['*'],
        'Access-Control-Expose-Headers' => [
            'X-Pagination-Per-Page',
            'X-Pagination-Total-Count',
            'X-Pagination-Current-Page',
            'X-Pagination-Page-Count',
        ],
    ],
];
/*re-set here*/
$behaviors['authenticator'] = [
    'class' => HttpBearerAuth::className(),
    'except' => ['options'],
];
$behaviors['contentNegotiator'] = [
    'class' => ContentNegotiator::className(),
    'formats' => [
        'application/json' => Response::FORMAT_JSON,
    ],
];

return $behaviors;

}

person Muhammad Arslan    schedule 09.05.2019