#node.js #express #jwt #bcrypt
#node.js #экспресс #jwt #bcrypt
Вопрос:
Я совсем новичок в Node.js / Экспресс и разработка веб-приложений. Я пытаюсь выполнить простую регистрацию пользователя, где я хэширую пароль с помощью bcrypt перед сохранением хэша в mongodb. Форма входа, которая должна позволять пользователю входить в систему, впоследствии выполняет поиск пользователя в БД, а затем сравнивает два пароля.
Некоторые маршруты в моем веб-приложении я хочу защитить, чтобы к ним имел доступ только аутентифицированный пользователь. Поэтому при успешном входе в систему я отправляю веб-токен Json (jwt) в заголовке ответа, который затем следует использовать — при перенаправлении на защищенный маршрут «/ lobby» — для аутентификации пользователя и разрешения ему / ей перейти к этому маршруту.
Однако я всегда получаю следующую ошибку:
Ошибка [ERR_HTTP_HEADERS_SENT]: не удается установить заголовки после их отправки клиенту
Похоже, что он уже отправляет ответ клиенту, прежде чем пытаться установить заголовок, который, конечно, больше невозможен.
Я был бы очень признателен за вашу помощь здесь!
Я использую следующий код:
Функция регистрации
async function register(req, res) {
//Check with user already exists
const emailExists = await User.findOne({email: req.body.email});
if(emailExists) {
return res.status(400).send('User already exists!');
};
//Hash the password and create new user from request data
bcrypt.hash(req.body.password, 10, async function (err, hashedPass){
if(err){
res.json({
error: err
});
}
let user = new User({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: hashedPass,
password2: hashedPass
});
try {
await user.save();
}catch (err) {
res.status(400).send(err);
};
});
res.render('index');
};
Функция входа в систему
async function login(req, res) {
const user = await User.findOne({email: req.body.email});
if(!user) {
return res.status(400).json({message: 'User not found!'}).render('index');
};
bcrypt.compare(req.body.password, user.password).then((result)=> {
if(result){
const token = jwt.sign({_id: user._id}, process.env.TOKEN_SECRET);
res.setHeader('auth-token', token.toString());
res.redirect('/lobby');
}else {
return res.status(400).json({message: 'Passwords do not match!'}).render('index');
}
}).catch((err)=> {
console.log(err);
});
};
В качестве промежуточного программного обеспечения для маршрута ‘/ lobby’ (т. Е. Когда кто-то выполняет запрос get в ‘/ lobby’) Я использую функцию «verifyToken», которая должна обеспечить правильную аутентификацию пользователя через jwt.
Функция verifyToken
const jwt = require('jsonwebtoken');
module.exports = function(req, res, next) {
console.log('verify function started');
const token = req.header('auth-token');
console.log(token);
if(!token) {
res.status(401).json({
message: 'Access denied!'
});
};
try {
const verified = jwt.verify(token, process.env.TOKEN_SECRET);
req.user = verified;
next();
}catch (err) {
res.status(400).json({
message: 'Invalid token!'
});
};
};
Как уже было сказано, я был бы очень признателен за вашу помощь здесь! Я предполагаю, что проблема намного проще, чем я думаю :-).
Приветствия
Комментарии:
1. зачем это нужно
res.render('index')
?, Если это успешный ответ, вам нужно перейти внутрьregister function
2. Я не уверен
res.status(400).json({message: 'Passwords do not match!'}).render('index')
, что звонюrender
послеjson()
. Я никогда этого не видел, можем ли мы так поступить?. И нет проблем сbcrypt
Ответ №1:
return
В некоторых случаях вы забыли ответ. Таким образом, он продолжает выполнять и другой код, поэтому сервер пытается снова отправить ответ, поэтому вы получаете эту ошибку.
Измените свой ответ следующим образом.
Функция verifyToken
const jwt = require('jsonwebtoken');
module.exports = function(req, res, next) {
console.log('verify function started');
const token = req.header('auth-token');
console.log(token);
if(!token) {
return res.status(401).json({ // <-- here you need to `return`
message: 'Access denied!'
});
};
try {
const verified = jwt.verify(token, process.env.TOKEN_SECRET);
req.user = verified;
next();
}catch (err) {
return res.status(400).json({
message: 'Invalid token!'
});
};
};
Функция регистрации
async function register(req, res) {
//Check with user already exists
const emailExists = await User.findOne({email: req.body.email});
if(emailExists) {
return res.status(400).send('User already exists!');
};
//Hash the password and create new user from request data
bcrypt.hash(req.body.password, 10, async function (err, hashedPass){
if(err) {
return res.json({ // <-- here as well
error: err
});
}
let user = new User({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: hashedPass,
password2: hashedPass
});
try {
await user.save();
return res.render('index'); // <-- assuming this is your success response
}catch (err) {
return res.status(400).send(err); <-- here too
};
});
};
Ответ №2:
Похоже, что в функции входа в систему устанавливается заголовок. Я вижу это через console.log(res.header('auth-token'));
. Впоследствии вызывается перенаправление на «/ lobby», поскольку запускается функция verifyToken.
Однако в функции verifyToken соответствующий заголовок тогда undefined
. Потому что я всегда также получаю 'Access denied!'
сообщение.
Как уже было сказано, я вызываю функцию verifyToken как промежуточное программное обеспечение при выполнении запроса get на маршрут / lobby. Маршрут для ‘/ lobby’ выглядит следующим образом:
const express = require('express');
const router = express.Router();
const lobbyCtrl = require('../controllers/lobby');
const verify = require('./verifyToken');
router.get('/', verify, lobbyCtrl.display);
module.exports = router;