问题
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