Angular and PHP project 'Access-Control-Allow-Origin' header contains multiple values

最后都变了- 提交于 2019-12-25 00:13:28

问题


I am working on a php and angular 6 based single page application.

The project runs normally except for today when I saw the following error at the console:

Access to XMLHttpRequest at 'http://dev.local/scripts/login.php' from origin 'http://localhost:4200' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:4200, *', but only one is allowed.

By dev.local I mean, the virtual host created using wampserver for testing purposes.

On login.php, I have the following scripts:

PHP call script:

<?php

require_once('../api.php');

//Getting username and password from Angular

$user = $_POST['username'];
$password = $_POST['password'];

$newApi = new api();
$conn = $newApi->connection();
//var_dump($conn);
$res = $newApi->login($conn, $user, $password);

echo json_encode($res);
?>

At the API:

<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: *');
header('Content-Type: application/json');
header('Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS');
header('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
error_reporting(E_ALL);

require_once('JWT.php');

include_once('../phpmailer/PHPMailer.php');
include_once('../phpmailer/POP3.php');
include_once('../phpmailer/SMTP.php');
include_once('../phpmailer/Exception.php');
class api {
    private $username ="root";
    private $password ="root";
    private $db="reg_sys";
    private $host = "localhost";
    public $conn;
    public $key = "key123";
    public $sessionJwt;
    public function connection(){
        session_start();
        try{
            $this->conn = new PDO("mysql:host=$this->host;dbname=$this->db", $this->username, $this->password);
            $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->conn->exec("SET CHARACTER SET utf8");

            return $this->conn;
        }
        catch(PDOException $e){
            return $e->getMessage();
        }

    }
public function login($conn, $user, $password){

        try{
            $exist = $this->checkIfUserExist($conn, $user);
            if($exist['exist'])
            {
                //Check Password and Generate a token
                $checkPassword = "SELECT user_id, user_name, user.role_id, roles.role_type 
                FROM user
                    LEFT JOIN roles ON user.role_id = roles.role_id 
                WHERE 
                    user_name = :user 
                AND 
                    user_password = :pass
                LIMIT 1";

                $execCheckPassword = $this->conn->prepare($checkPassword);
                $execCheckPassword->bindValue('user', $user);
                $execCheckPassword->bindValue('pass', $password);
                $execCheckPassword->execute();
                $fetchRes = $execCheckPassword->fetch();
                $resFound = $execCheckPassword->rowCount();
                //Then
                if($resFound>0)
                {
                    //Generate a JWT
                    //Array to generate a JWT from

                    $arrayJWT = 
                    [
                        'login_id'=>$fetchRes['user_id'],
                        'username'=> $fetchRes['user_name'], 
                        'user_role'=>$fetchRes['role_type']
                    ];

                    $encodedJWT = JWT::encode($arrayJWT, $this->key);

                    $resArray = 
                    [
                        'jwt'=> $encodedJWT,
                        'user_exist'=> 'true', 
                        'user_id'=>$fetchRes['user_id'],  
                        'username'=> $fetchRes['user_name'], 
                        'user_role'=>$fetchRes['role_type']
                    ];

                    $_SESSION['jwt']=$encodedJWT;


                }
                else
                {
                    $resArray = ['user_exist'=> 'false', 'errorMsg' => "Incorrect Password!!!"];
                    //Insert into login_attempt table
                    $sql = "INSERT INTO login_attempt(login_attempt_date, login_attempt_status, user_id)
                            VALUES(:date_time, :attempt_status, :user_id)";
                    $exec = $conn->prepare($sql);
                    $exec->bindValue(':date_time', $this->currentDateTime);
                    $exec->bindValue(':attempt_status', 'Active');
                    $exec->bindValue(':user_id', $exist['user_id']);
                    $exec->execute();
                }
            }
            else
            {
                $resArray = ['user_exist'=> 'false', 'errorMsg' => "Username doesn't exist"];
            }
            return $resArray;
        }
        catch(PDOException $e)
        {
            echo $e->getMessage();
        }



    }
}

At the angular side:

login(username, password): Observable<any> {
    let headerOptions = new HttpHeaders();
    //headerOptions.append('Access-Control-Allow-Origin', '*');
    //headerOptions.append('Access-Control-Request-Headers', '*');
    headerOptions.append('Access-Control-Allow-Credentials', 'true');
    headerOptions.append('Content-Type', 'application/json');
    headerOptions.append('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH,OPTIONS');
    headerOptions.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');


    this.credentials = { user: username, pass: password };
    const httpParams = new HttpParams()
      .set('username', username)
      .set('password', password);


    return this.http.post(this.globalVar.login, httpParams, {
      headers: headerOptions,
    })
  }

As you see, I commented the following:

//headerOptions.append('Access-Control-Allow-Origin', '*');
//headerOptions.append('Access-Control-Request-Headers', '*');

At the httpd-vhosts.conf:

# Virtual Hosts
#
<VirtualHost *:80>
  ServerName localhost
  ServerAlias localhost
  DocumentRoot "${INSTALL_DIR}/www"
  <Directory "${INSTALL_DIR}/www/">
    Options +Indexes +Includes +FollowSymLinks +MultiViews
    Header set Access-Control-Allow-Origin "*"
    AllowOverride All
    Require local
    Allow from 127.0.0.1
    Allow from 192.168.10.0
    Allow from 192.168.0.217
    Require all granted
  </Directory>
</VirtualHost>


#dev.local
<VirtualHost *:80>

    ServerAdmin it@m.org
    DocumentRoot "c:/wamp64/www/dev"
    ServerName dev.local    
    ServerAlias www.dev.local

    <Directory  "c:/wamp64/www/dev/">

        AllowOverride All
        Require local
        Allow from 127.0.0.1
        #Allow from 192.168.10.0
        #Allow from 192.168.0.140
        Require ip 192.168.0
        Require ip 192.168.1    
        Require ip 192.168.10
        Require all granted     
                Allow from all
    </Directory>
</VirtualHost>

And I enabled the mod_headers in httpd.conf.

I tried the solution from this question on stack, and this answer too, but nothing changed, and still getting the same error.

XHRResponse:

fetch("http://dev.local/scripts/login.php", {"credentials":"omit","headers":{"accept":"application/json, text/plain, /","content-type":"application/x-www-form-urlencoded;charset=UTF-8"},"referrer":"http://localhost:4200/login","referrerPolicy":"no-referrer-when-downgrade","body":"username=test&password=test1","method":"POST","mode":"cors"});


回答1:


I have faced this problem quite a few times while development with backends. On the server, when your application is hosted, you can be on the same domain by using the url in service as \login instead of http://localhost:8000/login.

For local development, use proxy config for running angular dev server. Please read https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md for the same. This will change the origin of /login from http://localhost:4200 to http://localhost:8000.

Update: (localhost:8000 is just an example, yours might be different) See the thing is browsers do not allow content from mixed sources (mixture of HTTP and HTTPS) and cross origin (data from different origin for XHR). So proxy is used in order to fool the browser.

Your application is sending \login to localhost:4200 and your proxy config in angular dev server is routing all the traffic to \login to localhost:8000 since \login API resides there. But the browser will see the you are sending request to localhost:4200\login so no more CORS issue. Your angular dev server will take care of routing your backend APIs. You just need to add the configurations for the same.

Feel free to ask more doubts, if you dont understand. Will try a different approach to explain.

Hopefully this will help you out. This will remove the issues of CORS.



来源:https://stackoverflow.com/questions/53167985/angular-and-php-project-access-control-allow-origin-header-contains-multiple-v

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!