我有一个带有passwordJS本地策略身份验证的快速应用程序。该应用程序已部署到Heroku的免费层(*.herokuapp.com
)。在生产中,将cookie.secure
设置为true
时,通过https不会调用deserializeUser
,也不会将cookie发送给客户端。
我知道这个问题已经问了很多,而且我已经寻找了一段时间。让我发疯的是,在实施了两天的不同解决方案之后,例如在this post中,我终于在昨天开始工作了。但是现在它不再工作了。我可以在日志中看到用户和会话对象,但是未调用deserializeUser
,req.isAuthenticated()
在passport.authenticate()
之外的任何地方都返回false,并且客户端中没有cookie。
可能有用的其他信息是,我正在使用Knex会话存储来存储会话,而我正在将PostgreSQL与Knex一起使用。
以下是相关代码:
servsr.js
const express = require('express');
const helmet = require('helmet');
const bodyParser = require('body-parser');
const cors = require('cors');
const passport = require('passport');
require('dotenv').config();
const dev_env = process.env.LOCAL_DEV;
const knex = require('knex')({
client: `pg`,
connection: {
host: dev_env ? process.env.DEV_DB_HOST : null,
user: dev_env ? process.env.DEV_USER : null,
password: dev_env ? process.env.DEV_PASSWORD : null,
database: dev_env ? process.env.DEV_DB_NAME : null,
connectionString: dev_env ? null : process.env.DATABASE_URL,
ssl: dev_env ? null : true
}
});
const app = express();
app.set('trust proxy', true);
app.use(cors({ origin: true, credentials: true }));
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Origin, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-Response-Time, X-PINGOTHER, X-CSRF-Token, Authorization');
if (req.method === "OPTIONS") {
return res.status(200).end();
} else {
next();
}
});
app.use(helmet());
app.use(bodyParser.json({ limit: '5mb' }));
require('./passport')(app);
const signin = require('./controllers/signin');
const uploadData = require('./controllers/uploadData');
const getData = require('./controllers/getData');
app.get('/', (req, res) => { res.send('its working!'); })
app.post('/signin', (req, res, next) => { signin.handleSignin(req, res, next, passport) })
// app.get('/success', (req, res) => res.status(200).json(true))
// app.get('/fail', (req, res) => res.status(400).json('fail'))
app.get('/log-out', (req, res) => {
req.logOut();
res.clearCookie(process.env.COOKIE_SECRET);
res.status(200).json('logged out ');
})
app.post('/upload-data', (req, res) => {
console.log('upload-data req.headers.cookie: ', req.headers.cookie)
console.log('upload-data req.protocol: ', req.protocol)
if (req.isAuthenticated()) {
uploadData.handleUploadData(req, res, knex)
} else {
res.status(400).json('request is not authenticated');
}
})
app.get('/get-data', (req, res) => {
console.log('get-data req.headers.cookie: ', req.headers.cookie)
console.log('get-data req.protocol: ', req.protocol)
if (req.isAuthenticated()) {
getData.handleDataRequest(req, res, knex)
} else {
res.status(400).json('request is not authenticated');
}
})
app.listen(process.env.PORT, () => {
console.log(`listening on port ${process.env.PORT}`);
})
passport.js
const session = require('express-session');
const passport = require('passport');
const bcrypt = require('bcrypt');
const KnexSessionStore = require('connect-session-knex')(session);
const dev_env = process.env.LOCAL_DEV;
const knex = require('knex')({
client: `pg`,
connection: {
host: dev_env ? process.env.DEV_DB_HOST : null,
user: dev_env ? process.env.DEV_USER : null,
password: dev_env ? process.env.DEV_PASSWORD : null,
database: dev_env ? process.env.DEV_DB_NAME : null,
connectionString: dev_env ? null : process.env.DATABASE_URL,
ssl: dev_env ? null : true
}
});
const store = new KnexSessionStore({ knex });
module.exports = (app) => {
app.use(session({
secret: process.env.COOKIE_SECRET,
saveUninitialized: false,
resave: false,
rolling: true,
store,
cookie: {
httpOnly: true,
maxAge: 1000 * 60 * 60 * 2, // 2 hours
secure: true,
// domain: process.env.DOMAIN,
path: '/'
}
}));
app.use(passport.initialize());
app.use(passport.session());
const LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy((username, password, done) => {
knex.select('id', 'name', 'hash').from('login')
.where('name', '=', username)
.then(user => {
if (!user) {
return done(null, false);
}
const isValid = bcrypt.compareSync(password, user[0].hash);
if (!isValid) {
return done(null, false);
}
return done(null, user[0]);
})
.catch(err => {
console.log(err);
return done(err);
})
}));
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
console.log('deserialize!')
knex.select('id').from('login')
.where('id', '=', id)
.then(id => {
done(null, id)
})
.catch(err => {
console.log(err)
done(err, null)
});
});
}
signin.js
const handleSignin = (req, res, next, passport) => {
console.log('signin req.headers.cookie: ', req.headers.cookie);
console.log('signin req.protocol: ', req.protocol);
const { username, password } = req.body;
if (!username || !password) {
return res.status(400).json('Incorrect form submission');
}
passport.authenticate('local', (err, user) => {
if (err) {
return next(err);
}
if (!user) {
return res.status(400).json('fail');
}
req.logIn(user, (err) => {
if (err) {
return next(err);
}
return res.status(200).json(true);
});
})(req, res, next)
}
module.exports = {
handleSignin
};
这是我的package.json
{
"name": "symbiosis-server",
"version": "1.0.0",
"description": "Server for industrial symbiosis data analysis app.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "nodemon server.js"
},
"author": "Ori Perelman",
"license": "ISC",
"devDependencies": {
"nodemon": "^1.19.1"
},
"dependencies": {
"bcrypt": "^3.0.6",
"body-parser": "^1.19.0",
"connect-session-knex": "^1.4.0",
"cors": "^2.8.5",
"dotenv": "^8.0.0",
"eslint": "^6.1.0",
"express": "^4.17.1",
"express-session": "^1.16.2",
"helmet": "^3.20.0",
"knex": "^0.19.0",
"passport": "^0.4.0",
"passport-local": "^1.0.0",
"pg": "^7.11.0"
}
}
正如我所说,这只是在昨天才有效,现在并非没有任何改变!所以我真的迷路了,不胜感激。 谢谢!
因此,在尝试查找问题之后,我在cookie.domain
设置中将其范围缩小到express-session
。设置为cookie.domain
时,该cookie由会话设置,我可以在数据库中看到它,但是由于某种原因,客户端未接收到它。我可以看到的另一件事是,设置了cookie.domain
之后,deserializeUser()
永远不会被调用。
至于身份验证先工作然后中断的问题,我能提出的唯一解释是,我显然忘记了删除某个地方的旧cookie,并且一直保持有效,直到过期...
我对代码做了一些修改,并在上面进行了更新。我进行的一项更改是添加app.set('trust proxy', true)
,因为我注意到req.protocol
返回http
而不是https
。它修复了协议,但没有解决问题。
因此,我想我将通过快速会话解决这个问题。但是,如果有人对cookie.domain
为何会破坏身份验证,甚至对解决方法有任何想法,我都会很乐意听到。
谢谢,我希望这可以对某人有所帮助。