Angular 和 PHP 项目 'Access-Control-Allow-Origin' header 包含多个值
Angular and PHP project 'Access-Control-Allow-Origin' header contains multiple values
我正在开发基于 php 和 angular 6 的单页应用程序。
项目运行正常,除了今天在控制台看到如下错误:
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.
dev.local
我的意思是,使用 wampserver 创建的虚拟主机用于测试目的。
在 login.php 上,我有以下脚本:
PHP调用脚本:
<?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);
?>
在 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();
}
}
}
angular侧:
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,
})
}
如您所见,我评论了以下内容:
//headerOptions.append('Access-Control-Allow-Origin', '*');
//headerOptions.append('Access-Control-Request-Headers', '*');
在 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>
并且我在 httpd.conf
中启用了 mod_headers。
我也尝试了这个 question on stack, and this 的解决方案,但没有任何改变,仍然出现同样的错误。
XHR 响应:
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"});
我在后台开发的时候遇到过好几次这个问题。在服务器上,当您的应用程序被托管时,您可以在同一个域中使用服务中的 url 作为 \login
而不是 http://localhost:8000/login
.
对于本地开发,请为 运行 angular 开发服务器使用代理配置。请阅读 https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md 同样的内容。这会将 /login
的来源从 http://localhost:4200
更改为 http://localhost:8000
。
更新:(localhost:8000 只是一个例子,你的可能不同)
请注意,浏览器不允许来自混合源(HTTP 和 HTTPS 的混合)和跨源(XHR 的不同源数据)的内容。所以使用proxy是为了骗过浏览器
您的应用程序正在将 \login
发送到 localhost:4200
并且您在 angular 开发服务器中的代理配置正在将所有流量路由到 \login
到 localhost:8000
因为\login
API 住在那里。但是浏览器会看到您正在向 localhost:4200\login
发送请求,因此不再有 CORS 问题。您的 angular 开发服务器将负责路由您的后端 API。您只需要添加相同的配置即可。
有不懂的可以多多提问。将尝试不同的方法来解释。
希望这对您有所帮助。这将消除 CORS 的问题。
我正在开发基于 php 和 angular 6 的单页应用程序。
项目运行正常,除了今天在控制台看到如下错误:
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.
dev.local
我的意思是,使用 wampserver 创建的虚拟主机用于测试目的。
在 login.php 上,我有以下脚本:
PHP调用脚本:
<?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);
?>
在 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();
}
}
}
angular侧:
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,
})
}
如您所见,我评论了以下内容:
//headerOptions.append('Access-Control-Allow-Origin', '*');
//headerOptions.append('Access-Control-Request-Headers', '*');
在 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>
并且我在 httpd.conf
中启用了 mod_headers。
我也尝试了这个 question on stack, and this
XHR 响应:
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"});
我在后台开发的时候遇到过好几次这个问题。在服务器上,当您的应用程序被托管时,您可以在同一个域中使用服务中的 url 作为 \login
而不是 http://localhost:8000/login
.
对于本地开发,请为 运行 angular 开发服务器使用代理配置。请阅读 https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md 同样的内容。这会将 /login
的来源从 http://localhost:4200
更改为 http://localhost:8000
。
更新:(localhost:8000 只是一个例子,你的可能不同) 请注意,浏览器不允许来自混合源(HTTP 和 HTTPS 的混合)和跨源(XHR 的不同源数据)的内容。所以使用proxy是为了骗过浏览器
您的应用程序正在将 \login
发送到 localhost:4200
并且您在 angular 开发服务器中的代理配置正在将所有流量路由到 \login
到 localhost:8000
因为\login
API 住在那里。但是浏览器会看到您正在向 localhost:4200\login
发送请求,因此不再有 CORS 问题。您的 angular 开发服务器将负责路由您的后端 API。您只需要添加相同的配置即可。
有不懂的可以多多提问。将尝试不同的方法来解释。
希望这对您有所帮助。这将消除 CORS 的问题。