Аутентификация с помощью bcrypt и jwt не работает

#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;