如何修复响应头的CORS错误

时间:2019-05-23 04:49:44

标签: angular yii2

我将Angular7用作前端,将yii2用作后端应用 当我尝试从角度调用api时,它给出了错误消息“ XMLHttpRequest at'http://localhost/pwap/html/api/web/v2/users/login'from origin'http://localhost:4200'”已被CORS策略阻止:对预检请求的响应未通过访问控制检查:它没有HTTP正常状态。”

我为CORS应用了不同的解决方案,但没有成功

我在Yii2后端的htaccess代码如下所示

Header set Access-Control-Allow-Origin "*"
#Header always set Access-Control-Allow-Origin: "http://localhost:4200"
Header set Access-Control-Allow-Methods "POST, GET, PUT, OPTIONS, DELETE"
Header set Access-Control-Max-Age "86400"
Header set Access-Control-Allow-Headers "*"
Header set Access-Control-Request-Headers "*"

如下所示的角度代码

        const httpOptions = {
            headers: new HttpHeaders({
              'Content-Type':  'application/x-www-form-urlencoded; charset=UTF-8, application/json, text/html, text/plain , text/xml',
              'app_token': environment.app_token,
              'Accept': '*/*',
              'Access-Control-Allow-Origin': ['*'],
              //'Access-Control-Request-Headers': ['*'],
              'Access-Control-Allow-Headers': ['*']
            })
          };

        let data = {
            "username": username,
            "password": password
        }
        let params = new URLSearchParams();
        for(let key in data){
            params.set(key, data[key]) 
        }


        return this.http.post('http://localhost/pwap/html/api/web/v2/users/login', data, httpOptions)
            .pipe().subscribe(user => {
                // login successful if there's a jwt token in the response
                if (user) {
                    console.log(user);
                    // store user details and jwt token in local storage to keep user logged in between page refreshes
                    localStorage.setItem('currentUser', JSON.stringify(user));
                    //this.currentUserSubject.next(user);
                }

                return user;
            }
            );
    }

我还传递了“ app_token”标头,该标头未显示在“键:值”对中 enter image description here

响应标题enter image description here

enter image description here

我在这里做什么错?需要帮助

3 个答案:

答案 0 :(得分:1)

我建议您使用解决方案来设置代理配置。使用angular-cli绕过代理。这样,与API服务器通信的就是Angular CLI服务器。

在项目文件夹中创建代理配置文件:proxy.config.json,其中包含以下内容。

{
 "/api/*": {
    "target": "http://http://localhost/pwap/html/api/web/v2/users/login",
    "secure": false,
    "pathRewrite": {"^/api" : ""}
  }
}

现在您可以使用以下命令为您的应用服务:

ng serve  —-proxy-config proxy.conf.json

请注意,您的请求必须转到“ localhost:4200 / app / {资源名称}”。例如,像这样:

this.httpClient.post('api/users'));

更多information...

答案 1 :(得分:0)

在api控制器中

public function behaviors()
{
    return yii\helpers\ArrayHelper::merge(parent::behaviors(), [
                'corsFilter' => [
                    'class' => yii\filters\Cors::className(),
                    'cors' => [
                        'Origin' => ['*'], // or your app adddress specified
                        'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'], 
                        'Access-Control-Request-Headers' => ['*'],
                        'Access-Control-Allow-Credentials' => true
                    ]
                ]
    ]);
}

答案 2 :(得分:0)

为了节省一些令人沮丧的长时间可能会有用,我在这里总结了两个对我有用的解决方案,使用 Angular (v11.0.2) 和 Yii2 (v2.0.35)。

使用用户名和密码进行身份验证

我的 OauthController 看起来像这样:

class OauthController extends Controller
{
    public static function allowedDomains() {
        return [
            'http://localhost:4200',
            'http://localhost:*',
            // '*',
        ];
    }

    public function behaviors() {
        return array_merge(parent::behaviors(), [

            // For cross-domain AJAX request
            'corsFilter'  => [
                'class' => \yii\filters\Cors::class,
                'cors'  => [
                    // restrict access to domains:
                    'Origin'                           => static::allowedDomains(),
                    'Access-Control-Request-Method'    => ['POST'],
                    'Access-Control-Allow-Headers'     => ['Origin', 'X-Requested-With', 'Content-Type', 'accept', 'Authorization'], 
                    'Access-Control-Allow-Credentials' => true,
                    'Access-Control-Max-Age'           => 3600,              
                ],
            ],

        ]);
    }
// ...
}

在 Angular 中,我登录添加标头 'Content-Type'Authorization(即 BasicAuth):

login(email: string, password: string) {
    let httpHeaders = new HttpHeaders();
    httpHeaders.append('Content-Type', 'application/json');

    httpHeaders.append(
      'Authorization',
      'Basic ' + btoa(email + ':' + password)
    );

    const httpOptions = {
      headers: httpHeaders,
    };

    return (
      this.http
        .post<AuthResponseData>(
          "http://your-url",
          // Body
          {
            username: email,
            password: password,
          },
          httpOptions
        )
        .pipe(
          // ...
        )
    );
}

使用不记名令牌进行身份验证

正如 this 的回答所指出的,Yii2 Bearer 身份验证可能很难用前端框架处理。

就我而言,任何使用 HttpBearerAuth 的尝试都失败了,我改用 QueryParamAuth

class SomeController extends \yii\rest\ActiveController
{

    public static function allowedDomains() {
        return [
            // '*', // THIS WILD CARD MAY CREATE PROBLEMS (see notes below)
            'http://localhost:4200',
            'http://localhost:*',
            $_SERVER["REMOTE_ADDR"],
        ];
    }

    public function behaviors()
    {
        $behaviors = parent::behaviors();
        $behaviors['authenticator'] = [
            'class' => QueryParamAuth::class,
        ];

        $behaviors['corsFilter'] = [
            'class' => \yii\filters\Cors::class,

            'cors'  => [
                'Origin'                           => static::allowedDomains(),
                'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
                'Access-Control-Allow-Headers'     => ['Origin', 'X-Requested-With', 'Content-Type', 'accept', 'Authorization'], 
                'Access-Control-Allow-Credentials' => true,
                'Access-Control-Allow-Credentials' => true,
                'Access-Control-Max-Age'           => 3600,
            ],
        ];

        return $behaviors;
    }
// ...
}

在 Angular 中,我在 access-token 中设置了查询参数 interceptor

let headers = req.headers.append('Content-Type', 'application/json');
const modifiedReq = req.clone({
  params: new HttpParams().set('access-token', user.token),
  headers: headers,
});

注意事项

  • 通过检查 prepareHeaders(),很明显只有当 '*''Access-Control-Allow-Credentials' 或未设置时,才允许使用通配符 false

  • 查询参数access-token是Yii2在$tokenParam类(source code)中设置的QueryParamAuth的定义

  • 如果有人知道如何使 HttpBearerAuth 与 Angular 一起工作,我将不胜感激。