如何解决yii2反应错误,已被CORS策略阻止:响应?

问题描述 投票:0回答:1

我有一个 yii2 应用程序作为后端,我正在使用 React 作为前端。 我现在尝试通过 api 调用登录功能。但是当我尝试在前端登录时出现此错误:

login/:1 Access to XMLHttpRequest at 'http://localhost:8080/v1/user/login' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

我已经尝试过了。我找到了这个链接:

https://www.yiiframework.com/doc/api/2.0/yii-filters-cors

但没有解决问题。

所以我尝试从 yii2 配置控制器,它可以与前端通信。

控制器:

<?php
namespace app\modules\v1\controllers;
use Yii;
use app\models\User;
use yii\rest\ActiveController;
use yii\web\Response;
use yii\filters\Cors;

class UserController extends ActiveController
{
    public $modelClass = 'app\models\User';

    protected $_verbs = ['POST','OPTIONS'];

    public function behaviors(){

       

        $behaviors = parent::behaviors();

         // remove auth filter before cors if you are using it
         unset($behaviors['authenticator']);

        // add CORS filter
        $behaviors['corsFilter'] = [
            'class' => \yii\filters\Cors::class,
            'cors' => [
                'Origin' => ['*'],

             'Access-Control-Allow-Origin' => ['*', 'http://localhost:3000'],

            'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],

            'Access-Control-Request-Headers' => ['*'],

            'Access-Control-Allow-Credentials' => true,

            'Access-Control-Max-Age' => 86400,

            'Access-Control-Expose-Headers' => ['X-Pagination-Current-Page'],
            ],
        ];
       
        $behaviors['authenticator']['except'] = ['options', 'login'];

        return $behaviors;
    }
 

    public function actionLogin()
    {
        $username = Yii::$app->request->post('username');
        $password = Yii::$app->request->post('password');

        $user = User::findByUsername($username);

        if ($user && $user->validatePassword($password)) {
            return ['status' => 'success', 'message' => 'Login successful'];
        } else {
            Yii::$app->response->statusCode = 401;
            return ['status' => 'error', 'message' => 'Invalid username or password'];
        }
    }


    public $username;
    public $password;
    public $rememberMe = true;
    private $_user;

     
    public function rules()
    {
        return [
           
            [['username', 'password'], 'required'],            
            ['rememberMe', 'boolean'],            
            ['password', 'validatePassword'],
        ];
    }


    /**
     * Validates the password.
     * This method serves as the inline validation for password.
     *
     * @param string $attribute the attribute currently being validated
     * @param array $params the additional name-value pairs given in the rule
     */
    public function validatePassword($attribute, $params)
    {
        if (!$this->hasErrors()) {
            $user = $this->getUser();
            if (!$user || !$user->validatePassword($this->password)) {
                $this->addError($attribute, 'Incorrect username or password.');
            }
        }
    }
  
    protected function getUser()
    {
        if ($this->_user === null) {
            $this->_user = User::findByUsername($this->username);
        }

        return $this->_user;
    }

和用户:

<?php
// phpcs:ignoreFile

namespace app\models;

use Yii;
use yii\base\NotSupportedException;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
use yii\filters\Cors;

/**
 * This is the model class for table "user".
 *
 */
class User extends \yii\db\ActiveRecord implements IdentityInterface
{

    const STATUS_DELETED = 0;
    const STATUS_INACTIVE = 9;
    const STATUS_ACTIVE = 10;
   
    public function behaviors()
    {
        return [
            TimestampBehavior::class,
        ];
    }


    public static function tableName()
    {
        return '{{%user}}';
    }

  
    public function rules()
    {
        return [
           
             
            [['status', 'created_at', 'updated_at'], 'default', 'value' => null],
            [['status', 'created_at', 'updated_at'], 'integer'],
            [['username', 'password_hash', 'password_reset_token', 'email', 'verification_token'], 'string', 'max' => 255],
            [['auth_key'], 'string', 'max' => 32],
            [['email'], 'unique'],
            [['password_reset_token'], 'unique'],
            [['username'], 'unique'], 
            ['status', 'default', 'value' => self::STATUS_ACTIVE],
            ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_INACTIVE, self::STATUS_DELETED]],
        ];
    }

     
    public static function findIdentity($id)
    {
        return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]);
    }

    
    public static function findIdentityByAccessToken($token, $type = null)
    {
        return static::findOne(['auth_key' => $token]);
    }

      
    public function getId()
    {
        return $this->getPrimaryKey();
    }

  
    public function getAuthKey()
    {
        return $this->auth_key;
    }
}

和 web.php 文件:

<?php
// phpcs:ignoreFile

$params = require __DIR__ . '/params.php';
$db = require __DIR__ . '/db.php';

$config = [
    'id' => 'basic',
    'name' => 'internetsuite 2.0',
    'basePath' => dirname(__DIR__),
    'bootstrap' => ['log'],
    'aliases' => [
        '@bower' => '@vendor/bower-asset',
        '@npm'   => '@vendor/npm-asset',
    ],
    'modules' => [
        'v1' => [
            'class' => 'app\modules\v1\Module',
        ],
    ],
    'components' => [
        'request' => [

            'parsers' => [
                'application/json' => 'yii\web\JsonParser',
            ],
            // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
            'cookieValidationKey' => 'OEtCunrAfQNETtmUSDnZw1JPHTB44i3A',
        ],
        

        'cache' => [
            'class' => 'yii\caching\FileCache',
        ],
        'user' => [
            'identityClass' => 'app\models\User',
            'enableAutoLogin' => true,
        ],
        'errorHandler' => [
            'errorAction' => 'site/error',
        ],
        'mailer' => [
            'class' => \yii\symfonymailer\Mailer::class,
            'viewPath' => '@app/mail',
            // send all mails to a file by default.
            'useFileTransport' => true,
        ],
        'log' => [
            'traceLevel' => YII_DEBUG ? 3 : 0,
            'targets' => [
                [
                    'class' => 'yii\log\FileTarget',
                    'levels' => ['error', 'warning'],
                ],
            ],
        ],
        'db' => $db,

    'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' =>  [
                'class' => 'yii\rest\UrlRule', 
                'controller' => 'v1/user',                 
                'patterns'=>[
                    'POST' => 'signup',
                    'POST login' => 'login',
                    'OPTIONS login' => 'options',

                ],
            
            ],                           
            
        ],

    ],
    'params' => $params,
];

if (YII_ENV_DEV) {
    // configuration adjustments for 'dev' environment
    $config['bootstrap'][] = 'debug';
    $config['modules']['debug'] = [
        'class' => 'yii\debug\Module',
        // uncomment the following to add your IP if you are not connecting from localhost.
        //'allowedIPs' => ['127.0.0.1', '::1'],
    ];

    $config['bootstrap'][] = 'gii';
    $config['modules']['gii'] = [
        'class' => 'yii\gii\Module',
        // uncomment the following to add your IP if you are not connecting from localhost.
        //'allowedIPs' => ['127.0.0.1', '::1'],
    ];
}

return $config;

并对登录调用做出反应:

/* eslint-disable newline-before-return */
import { UserProfileToken } from "../models/User";
import axios from "axios";
import { handleError } from "../helpers/ErrorHandler";

const api = "http://localhost:8080/v1/";

export const loginAPI = async (username: string, password: string) => {
  try {
    const data = await axios.post<UserProfileToken>(api + "user/login", {
      username: username,
      password: password,
    });
    return data;

  } catch (error) {
    handleError(error);
  }
};
}

问题:如何解决错误:

login/:1 Access to XMLHttpRequest at 'http://localhost:8080/v1/user/login' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
php yii2 cors
1个回答
0
投票

您的

UrlManager
规则设置存在一些问题。尽管使用了
yii\filters\Cors
,请求仍被 CORS 策略阻止也是这些问题的结果。

首先,这部分配置:

[
    'class' => 'yii\rest\UrlRule',
    'controller' => 'v1/user',
    'POST' => 'v1/user/signup',
]

'POST' => 'v1/user/signup'
部分不起作用。我不确定您在复制代码进行提问时是否遗漏了太多内容,或者这是否就是您在项目中进行设置的方式。问题是,在
yii\rest\UrlRule
配置中,将方法与操作匹配必须是
$patterns
$extraPatterns
属性的一部分。这两个属性之间的主要区别在于
$patterns
包含
yii\rest\ActiveConroller
中的操作使用的一些默认模式。 因此其余规则配置应如下所示:

[
    'class' => 'yii\rest\UrlRule',
    'controller' => 'v1/user',
    'patterns' => [
        'POST' => 'signup',
        // ... patterns for other HTTP methods
    ]
]

注册看起来有效的原因可能是因为请求与默认的发布模式匹配。看起来像这样

'POST' => 'create'
。用户的创建由
yii\rest\CreateAction
处理,它是默认
yii\rest\ActiveController
actions 的一部分。

另一个问题是,如果您希望休息控制器处理类似于

module/controller/action
(例如
v1/user/login
)格式的 url,您必须明确添加此路由的模式。这是因为
yii\rest\UrlRule
不使用通常的
controller/action
匹配标准规则。 因此,通过添加
v1/user/login
POST 请求模式,配置可能如下所示:

[
    'class' => 'yii\rest\UrlRule',
    'controller' => 'v1/user',
    'patterns' => [
        'POST' => 'signup',
        'POST login' => 'login',
        // ... patterns for other HTTP methods
    ]
]

现在我们终于进入 CORS 阻塞部分了。当浏览器要发送 AJAX POST 请求时,它所做的第一件事就是所谓的“CORS 预检”。这意味着它会尝试将 OPTIONS 请求发送到将首先发送 POST 请求以检索 CORS 标头的同一 URL。您的

UrlManager
不知道如何处理
v1/user/login
路线的 OPTIONS 请求。该请求未到达您的
UserController
,并且
yii\filters\Cors
永远不会应用于该请求。由于浏览器无法获取正确的 CORS 标头,并且 POST 请求被阻止。 为了避免这种情况,我们需要将 OPTIONS 请求的模式添加到我们的配置中,例如如下所示:

[
    'class' => 'yii\rest\UrlRule',
    'controller' => 'v1/user',
    'patterns' => [
        'POST' => 'signup',
        'POST login' => 'login',
        'OPTIONS login' => 'options',
        // ... patterns for other HTTP methods
    ]
]

为了避免 CORS 阻塞,使用 options

 默认操作中已经存在的 
yii\rest\ActiveController
 操作就足够了。对于正确的 REST API,您可能需要确保仅返回受支持的方法来响应 OPTIONS 请求。

© www.soinside.com 2019 - 2024. All rights reserved.