使用 MEAN 堆栈应用程序(托管在 Heroku 上)消除 CORS 策略错误
Posted
技术标签:
【中文标题】使用 MEAN 堆栈应用程序(托管在 Heroku 上)消除 CORS 策略错误【英文标题】:Getting rid of CORS policy error with MEAN stack app (hosted on Heroku) 【发布时间】:2021-02-13 04:04:05 【问题描述】:我已经在 MEAN stack 应用程序上工作了一段时间,在过去的一个月里,我一直在努力解决这个 CORS 政策错误:
“从源访问
我已经一些成功地让它在一些设备上工作。在我的 server.js 文件中的中间件函数中的请求/响应标头设置和与后端通信的服务中的 POST 请求搞乱之后,我已经让它在我的桌面(Windows 10)上工作,在我的 Ubuntu 操作系统上我的台式机、笔记本电脑 (Windows 10)、我朋友的 macbook 等。
但是,我的一些朋友设备上仍然存在 CORS 政策问题。哪些设备工作和哪些设备不工作似乎没有任何共同点(不同的操作系统工作和不工作,当它不工作时,它适用于所有浏览器)。
还可能值得注意的是,在出现 CORS 错误的设备上,我的应用页面右上角会显示“不安全”,而在它工作的设备上它被标记为安全。
我做过的事情:
在我的 server.js 文件中导入了 cors 模块 尝试使用 csp(内容安全策略)模块 修改了对我的服务器的请求的标头 修改了从服务器发送回客户端的响应的标头 大量搜索和摆弄 CORS 策略设置如果相关的话,我还使用护照和 bcrypt 进行用户身份验证。
这里是相关的 server.js 代码:
var express = require('express');
var cors = require('cors');
//var csp = require('content-security-policy');
var app = express();
var dotenv = require('dotenv');
dotenv.config();
var url = require('url');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session'); // should give us persistent sessions...
var passport = require('passport');
var ejs = require('ejs');
var bcrypt = require('bcrypt');
var flash = require('express-flash');
var mongoose = require('mongoose');
var path = require('path');
//const env = require('./src/environments/environment');
//const methodOverride = require('method-override')
//var initializePassport = require('passport-config');
var initializePassport = require('./passport-config');
// this completes passport authentication strategy
// passes passport, a function for finding a user by their username,
// and a function for finding a user by id to the initialize() function inside passport-config
// the authenticateUser function then uses these methods to get what it needs
var connurl = '';
if(process.env.NODE_ENV == 'development')
connurl = 'http://localhost:4200';
else
connurl = 'https://to-do-bentancock.herokuapp.com';
initializePassport(
passport,
// both of these things are functions, passed into passport config
// I think I pass mongoose middleware stuff here to return the right things
username => Users.find(username: $eq: username ),
id => Users.find(id: $eq: id )
)
var MongoStore = require('connect-mongo')(session);
var router = express.Router();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded(extended: false));
app.use(cookieParser('process.env.SECRET'));
var cspPolicy =
'default-src': 'self, https://to-do-bentancock.herokuapp.com/*',
'img-src': '*',
/*app.use(csp(
policies:
'default-src': [csp.NONE],
'img-src': [csp.SELF],
));
const globalCSP = csp.getCSP(cspPolicy);
app.use(globalCSP)
*/
app.use(session(
secret: 'process.env.SECRET',
resave: true, // should we reset our session variables if nothing has changed?
// NOTE: this MUST be set to true otherwise the user authentication / session data won't be saved between middleware methods
// e.g. if you log in (via /tasks post method), it will print the session data at the end, but if you then do '/create' method right after the req object will be null (because it wasn't saved)
saveUninitialized: true, // do you want to save an empty value in the session if there is no value?
cookie:
// might want to look into changing this in the future, as cookie stores user stuff
// for now I have it off until I'm certain I've got all this passport js, cookie and session stuff down pat
secure: false,
maxAge: 600000
,
store: new MongoStore(
mongooseConnection: mongoose.connection,
ttl: 60 * 60, // keeps the session open for 1 hour
collection: 'sessions'
)
));
app.use(passport.initialize());
app.use(passport.session());
app.use(cors(credentials: true, origin: true));
// enables pre-flight requests across the board
app.options('*', cors()) // include before other routes
app.get('/with-cors', cors(), (req, res, next) =>
console.log("testing cors:");
);
app.use('/', express.query());
app.get('/*', function(req,res)
res.header("Access-Control-Allow-Origin", connurl);
res.header('Access-Control-Allow-Credentials', true);
//res.header('Content-Type', 'application/json');
console.log("here's what app.get is receiving: " + req.url);
console.log("sending file!");
res.sendFile(path.join(__dirname + '/dist/to-do-heroku/index.html'));
);
app.post('/loginCheck', function(req, res)
res.header("Access-Control-Allow-Origin", connurl);
res.header('Access-Control-Allow-Credentials', true);
res.header('Content-Type', 'application/json');
console.log("res header: %j", res.getHeaders());
console.log("\nlogin check");
if(req.isAuthenticated())
console.log("authentication returns true!");
//console.log("printing req passport data: ");
//console.log(req.session);
//console.log(req.user);
//res.headersSent();
res.send(authenticated: true);
else
console.log("user is not authenticated");
res.send(authenticated: false);
);
app.post('/login', function(req, res, next)
res.header("Access-Control-Allow-Origin", connurl);
res.header('Access-Control-Allow-Credentials', true);
res.header('Content-Type', 'application/json');
console.log("res header: %j", res.getHeaders());
passport.authenticate('local', function(err, user, info)
console.log("printing error: " + err);
console.log("passport info: " + JSON.stringify(info)); // undefined
if(err)
console.log("error authenticating!");
res.send(status: 'error logging in user');
return;
if(!err)
req.logIn(user, function(err)
if(err)
console.log("error logging in");
//return
res.send(status: 'success');
);
)(req, res, next);
);
app.post('/logout', checkAuthenticated, async function(req, res)
res.header("Access-Control-Allow-Origin", connurl);
res.header('Access-Control-Allow-Credentials', true);
res.header('Content-Type', 'application/json');
console.log("\nlogging out user");
await req.logout(); // logOut or logout??
res.send(status: 'redirect', url: '/login');
);
app.post('/getTasks', checkAuthenticated, function(req, res)
res.header("Access-Control-Allow-Origin", connurl);
res.header('Access-Control-Allow-Credentials', true);
res.header('Content-Type', 'application/json');
console.log("\n Successful authentication, request: " + JSON.stringify(req.body));
Users.find(id: $eq: req.session.passport.user, function(err, doc)
if(!doc.length || doc == null) // if the user is not found
console.log("ERROR: USER NOT FOUND, LOGGING OUT");
req.logOut();
res.send(error:'not found'); // send some kind of message back to client-side
else
res.send(tasks: doc[0].tasks, idCount: doc[0].idCount);
);
);
var uri = '';
if(process.env.NODE_ENV == 'development')
uri = 'mongodb://localhost/todoDB'
else
uri = "mongodb+srv://todoApp:7211@cluster0.huawl.mongodb.net/toDoDB?retryWrites=true&w=majority";
//const uri = "mongodb+srv://todoApp:7211@cluster0.huawl.mongodb.net/toDoDB?retryWrites=true&w=majority";
mongoose.connect(uri);
const connection = mongoose.connection;
connection.once('open', function()
mongoose.connection.db.collection('usersCollection').countDocuments(function(err, docs)
console.log("there are " + docs + " docs in the collection\n");
);
console.log("MongoDB database connection established successfully\n");
);
function checkAuthenticated(req, res, next)
if (req.isAuthenticated())
console.log("user is authenticated!");
return next();
console.log("WARNING: USER NOT AUTHENTICATED");
res.send(authenticated: false);
function checkNotAuthenticated(req, res, next)
if (req.isAuthenticated())
console.log("\nuser IS authenticated, stopping this request...");
// Send a message back to the client telling it to redirect instead
//res.send(authenticated: true);
return;
//res.send(authenticated: false);
console.log("user is NOT authenticated");
next();
if(process.env.NODE_ENV == 'development')
app.listen(4000, function(req, res)
console.log("express server listening on port 4000");
);
else
app.listen(process.env.PORT || 8080, function(req, res)
console.log("express server listening on port 8080");
);
这是与 server.js 通信的服务的代码:
auth.service:
import Injectable from '@angular/core';
import HttpClient, HttpHeaders from '@angular/common/http';
import Router, ActivatedRoute, ParamMap from '@angular/router';
import Subject from 'rxjs';
import environment from './../environments/environment';
@Injectable(
providedIn: 'root'
)
export class AuthService
url = environment.apiUrl;
username;
password;
loggedIn = false;
constructor(private http: HttpClient, private router: Router)
login(uname, pw)
console.log("logging in user: " + uname + " " + pw + '\n');
console.log("the url: " + this.url);
this.username = uname;
this.password = pw;
return this.http.post(this.url + '/login', username: uname, password: pw,
headers: new HttpHeaders(
'Access-Control-Allow-Credentials' : 'true',
'Access-Control-Allow-Origin': "*" // this might just need to be the api url
),
withCredentials: true
);
setLogin(bool)
this.loggedIn = bool;
logout()
console.log("test auth logout");
return this.http.post(this.url + '/logout', body: 'logout',
headers: new HttpHeaders(
'Content-Type' : 'application/json',
'Access-Control-Allow-Credentials' : 'true',
'Access-Control-Allow-Origin': "*"
),
withCredentials: true
);
loginCheck()
console.log("test auth service login check");
return this.http.post(this.url + '/loginCheck',
headers: new HttpHeaders(
'Content-Type' : 'application/json',
'Access-Control-Allow-Credentials' : 'true',
'Access-Control-Allow-Origin': "*"
),
withCredentials: true
);
registerUser(uname, pw)
console.log("registering user: " + uname + " " + pw + '\n');
// the object sent needs to be a user object, which contains task objects
// new user won't have any tasks tho (obvi)
// also need to check for duplicate usernames
return this.http.post(this.url + '/register',
username: uname,
password: pw,
idCount: 0,
id: Date.now().toString(),
tasks: []
,
headers: new HttpHeaders(
'Content-Type' : 'application/json',
'Access-Control-Allow-Credentials' : 'true',
'Access-Control-Allow-Origin': "*"
),
withCredentials: true
);
// takes user tasks data as input, passes it to tasks component
renderTasks()
console.log("render tasks \n");
this.router.navigate(['/tasks']);
task.service:
import Injectable from '@angular/core';
import HttpClient, HttpHeaders from '@angular/common/http';
import Observable from 'rxjs';
import catchError, tap, share, map from 'rxjs/operators';
import environment from './../environments/environment';
@Injectable(
providedIn: 'root'
)
export class TasksService
//url = 'http://localhost:4000' // the port the mongo database is listening on
//url = 'https://to-do-bentancock.herokuapp.com';
url = environment.apiUrl;
constructor(private http: HttpClient)
getTasks(userName, pw)
console.log("tasks service: get tasks \n");
console.log("username and pw to post: " + userName + " " + pw);
return this.http.post(this.url + '/getTasks', username: userName, password: pw,
headers: new HttpHeaders(
'Content-Type' : 'application/json',
'Access-Control-Allow-Credentials' : 'true',
'Access-Control-Allow-Origin': "*"
),
withCredentials: true
);
deleteTask(uname, pw, id: number )
var delUrl = this.url + "/deleteTask/" + id;
return this.http.post(delUrl, username: uname, password: pw,
headers: new HttpHeaders(
'Content-Type' : 'application/json',
'Access-Control-Allow-Credentials' : 'true',
'Access-Control-Allow-Origin': "*"
),
withCredentials: true
);
/*
a task object has a:
- name
- date
- description
- priority
- id
*/
createTask(uname, pw, task)
console.log("test create task (service)");
// send user data, and a task object
return this.http.post(this.url + '/create',
task:
name: task.name,
date: task.date,
description: task.description,
priority: task.priority,
id: task.id,
state: task.state
,
user:
username: uname,
password: pw
,
headers: new HttpHeaders(
'Content-Type' : 'application/json',
'Access-Control-Allow-Credentials' : 'true',
'Access-Control-Allow-Origin': "*"
),
withCredentials: true
);
我一直在谷歌上搜索并排除故障,我已经筋疲力尽了。有人有建议吗?我错过了什么?
【问题讨论】:
【参考方案1】:我只是在你的 get 中使用 app.use(cors())
而没有 app.options('*', cors())
和 cors()
【讨论】:
以上是关于使用 MEAN 堆栈应用程序(托管在 Heroku 上)消除 CORS 策略错误的主要内容,如果未能解决你的问题,请参考以下文章
Heroku + MEAN 堆栈错误:参数“url”必须是字符串,而不是未定义