Как отправить токен jwt на защищенный маршрут в node.js - PullRequest
3 голосов
/ 06 мая 2020

Я создал форму входа в систему, которая должна перенаправлять пользователя на страницу панели инструментов в случае, если он вводит правильный пароль и имя пользователя. Если пользователь пытается перейти к URL-адресу панели мониторинга без входа в систему, страница не должна отображаться, поскольку это защищенный маршрут. Я пытаюсь отправить токен jwt, когда пользователь входит в систему, но это не работает. Я просто получаю сообщение Forbidden, когда я вхожу в систему, поэтому кажется, что токен отправляется неправильно, как я могу отправить токен jwt и получить доступ защищенный маршрут после успешного входа пользователя?

Вот мой сервер. js:

const express = require('express');
const jwt = require('jsonwebtoken');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
let Post = require('./models/post.model.js');
const app = express();
const cors = require('cors');
require('dotenv').config();

app.use(cors());
app.use("/assets", express.static(__dirname + "/assets"));
app.use(bodyParser.urlencoded({ extended: true }));
const BASE_URL = process.env.BASE_URL;

const PORT = process.env.PORT || 1337;
mongoose.connect(BASE_URL, { useNewUrlParser: true, useUnifiedTopology: true })

const connection = mongoose.connection;

connection.once('open', function () {
    console.log('Connection to MongoDB established succesfully!');
});

app.set('view-engine', 'ejs');

app.get('/', (req, res) => {
    res.render('index.ejs');
});

app.post('/', (req, res) => {
    let username = req.body.username;
    let password = req.body.password;

    const user = {
        username: username,
        password: password
    }

    jwt.sign({ user }, process.env.SECRET_KEY, (err, token) => {
        res.json({
            token
        })
    });

    if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
        res.json('Invalid credentials');
    } else {
        res.setHeader('Authorization', 'Bearer '+ token);
        res.redirect('/dashboard')
    }

});

app.get('/dashboard', verifyToken, (req, res) => {
    jwt.verify(req.token, process.env.SECRET_KEY, (err, authData) => {
        if (err) {
            res.sendStatus(403);
        } else {
            res.sendStatus(200);
        }
    });
    res.render('dashboard.ejs');
});

app.get('/dashboard/createPost', verifyToken, (req, res) => {
    res.render('post.ejs');
});

app.post('/dashboard/createPost', async (req, res) => {
    let collection = connection.collection(process.env.POSTS_WITH_TAGS);
    res.setHeader('Content-Type', 'application/json');
    let post = new Post(req.body);
    collection.insertOne(post)
        .then(post => {
            res.redirect('/dashboard')
        })
        .catch(err => {
            res.status(400).send(err);
        });
});

// TOKEN FORMAT
// Authorization: Bearer <access_token>

//Verifing the Token
function verifyToken(req, res, next) {
    // Get auth header value
    const bearerHeader = req.headers['authorization'];
    // Check if bearer is undefined
    if (typeof bearerHeader !== 'undefined') {
        // Spliting the bearer
        const bearer = bearerHeader.split(' ');
        // Get token from array
        const bearerToken = bearer[1];
        // Set the token
        req.token = bearerToken;
        // Next middleware
        next();

    } else {
        // Forbid the route
        res.sendStatus(403);
    }

}

app.listen(PORT);

Ответы [ 4 ]

2 голосов
/ 10 мая 2020

Ваша проблема в том, что ваша переменная token доступна только внутри обратного вызова для вызова jwt.sign, поэтому, когда вы попытаетесь сделать это здесь res.setHeader('Authorization', 'Bearer '+ token);, он не будет знать, на какую переменную вы ссылаетесь , следовательно, неопределенная ошибка. Кстати, если вы собираетесь использовать jwt.sign асинхронно, то код, который его использует, также должен быть внутри обратного вызова, иначе синхронный код вне обратного вызова, скорее всего, будет выполняться первым (и, следовательно, не сможет получить доступ к любые результаты асинхронного кода), поскольку асинхронный обратный вызов выполняется в фоновом режиме. Решение здесь состоит в том, чтобы либо переключить ваше использование на синхронное использование, либо поместить код ответа внутри обратного вызова. Кроме того, вызов res.json завершит ответ, поэтому я не уверен, что именно вы пытаетесь выполнить sh с помощью вызовов множественного ответа

Синхронная версия:

app.post('/', (req, res) => {
    let username = req.body.username;
    let password = req.body.password;

    const user = {
        username: username,
        password: password
    };

    let token = undefined;
    try {
        token = jwt.sign({ user }, process.env.SECRET_KEY);
    } catch (e) {
        // handle error
    }

    if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
        res.json('Invalid credentials');
    } else {
        res.setHeader('Authorization', 'Bearer '+ token);
        res.redirect('/dashboard');
    }

});

Асинхронная версия:

app.post('/', (req, res) => {
    let username = req.body.username;
    let password = req.body.password;

    const user = {
        username: username,
        password: password
    }

    jwt.sign({ user }, process.env.SECRET_KEY, (err, token) => {
        if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
            res.json('Invalid credentials');
        } else {
            res.setHeader('Authorization', 'Bearer '+ token);
            res.redirect('/dashboard')
        }
    });

});

В этих примерах я выбрал res.json({ token }), потому что вы не можете использовать res.json и затем выполнить перенаправление, но измените эти части так, как они лучше всего подходят вашему коду. С другой стороны, вы, вероятно, не захотите включать пароль в свой токен, потому что, хотя JWT (при использовании алгоритмов по умолчанию / стандартных, которые не включают шифрование) криптографически гарантированно не поддаются изменению, они все еще читаемые

2 голосов
/ 10 мая 2020

см. Этот пример, я использую промежуточное ПО (checkAuthLogin), этот код содержит все, что нужно для вашего вопроса:

index. js:

const express = require('express');
const app = express();
require('./db/mongoose');

const userRouter = require('./routers/user');


app.use(express.json());
app.use(userRouter);


app.listen(3000, ()=> { 
    console.log('Server is up on port ', 3000)
});

db / mon goose . js:

const mongoose = require('mongoose');

mongoose.connect("mongodb://127.0.0.1:27017/db-test" {
    useNewUrlParser : true,
    useCreateIndex : true,
    useFindAndModify : false,
    useUnifiedTopology: true
});

маршрутизаторов / пользователь. js:

const express = require('express');
const router = new express.Router();
const RootUser = require('../models/root-user');
const {checkRootLogin} = require('../middleware/checkAuthLogin');

router.post('/createrootuser', async (req, res) => {

    const updates = Object.keys(req.body);
    const allowedUpdatesArray = ['name', 'password'];
    const isValidOperation = updates.every((update) => allowedUpdatesArray.includes(update));

    if (!isValidOperation) {
        return res.status(400).send({error: 'Invalid Request Body'})
    }

    const rootUser = new RootUser(req.body);

    try {
        await rootUser.save();
        // sendWelcomeEmail(user.email, user.name)
        const token = await rootUser.generateAuthToken();
        //console.log(user)
        res.status(201).send({rootUser, token});
    } catch (e) {
        res.status(400).send(e)
    }

});

//use this middleware(checkRootLogin) for check root user can access this function
router.post('/rootconfig', checkRootLogin, async (req, res) => {

        res.status(200).send({success: 'success add root config'})

});

module.exports = router;

модель / root -пользователь. js:

const mongoose = require('mongoose');
const validator = require('validator');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');    

const userRootSchema = new mongoose.Schema({
    name: {
        type : String,
        required: true,
        unique : true,
        trim : true,
        lowercase : true,
    },
    password: {
        type : String,
        required: true,
        unique : true,
        trim : true,
        lowercase : true,
        minlength : 6,
        validate (value) {
            //if (validator.contains(value.toLowerCase(), 'password')){
            if (value.toLowerCase().includes('password')){
                throw new Error('Password can not contained "password"')
            }
        }
    },

    tokens : [{
        token : {
            type : String ,
            required : true
        }
    }],

}, {
    timestamps: true
});


userRootSchema.methods.generateAuthToken = async function(){

    const root = this;
    // generate token
    try {
        // const token = jwt.sign({ _id : user._id.toString()}, process.env.JWT_SECRET);
        const token = jwt.sign({ _id : root._id.toString()}, "test");
        // add token to user model
        root.tokens = root.tokens.concat({ token });
        await root.save();
        return token
    } catch (e){
        throw new Error(e)
    }

};



userRootSchema.pre('save', async function(next){
    // this give ccess to individual user
    const user = this;

    if (user.isModified('password')){
        user.password = await bcrypt.hash(user.password, 8)
    }
    next()

});

const UserRoot = mongoose.model('UserRoot', userRootSchema);

module.exports = UserRoot;

промежуточное ПО / checkAuthLogin. js:

const jwt = require('jsonwebtoken');
const RootUser = require('../models/root-user');  

const checkRootLogin = async (req, res, next) => {
    try {
        const token = req.header('Authorization').replace('Bearer ', '');
        // const decoded = jwt.verify(token, process.env.JWT_SECRET);
        const decoded = jwt.verify(token, "test");

        const rootUser = await RootUser.findOne({_id: decoded._id, 'tokens.token': token});

        if (!rootUser) {
            throw new Error("User cannot find!!");
        }

        req.token = token;
        req.rootUser = rootUser;
        req.userID = rootUser._id;
        next()
    } catch (e) {
        res.status(401).send({error: 'Authentication problem!!'})
    }
};

module.exports = {checkRootLogin};
1 голос
/ 10 мая 2020

У меня есть одно решение для отправки токена jwt, но вам нужно будет установить еще один пакет.

Я использую express только для backend api. Но вы можете использовать тот же logi c, примененный здесь к вашему приложению.

Библиотека, которую вам нужно будет установить, это express -jwt

Он обрабатывает маршруты для блокировки доступа к конечной точке, требующей аутентификации.

сервер. js

require('dotenv').config()
const express = require('express');
const logger = require('morgan');
const cors = require('cors');
const jwt = require('jsonwebtoken');
const expressJwt = require('express-jwt');

const app = express();

cors({ credentials: true, origin: true });
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use('/secure', expressJwt({ secret: process.env.SECRET }));
app.use(require('./server/index'));

app.get('/secure/dashboard') => {
    //now you can only access this route with authorization header
    //prependending the '/secure/ to new routes should make them return 401 when accessed without authorization token
    //accessing this route without returns 401.
    //there is no need to validate because express-jwt is handling.
    console.log(res.user)//should print current user and pass signed with token
    res.render('dashboard.ejs');
});

app.post('/', (req, res) => {
    let username = req.body.username;
    let password = req.body.password;
    //jwt.sign({ user }, process.env.SECRET_KEY, (err, token) => {
    //    res.json({
    //        token
    //    })
    //});
    //shouldn't sign json here, because there is no guarantee this is a valid
    //username and password it can be an impostor

    if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
        res.json('Invalid credentials');
    } else {
        const user = {
           username: username,
           password: password
        };
        const tk = {};
        tk.token = 'Bearer ' + jwt.sign(user, process.env.SECRET_KEY, { expiresIn: 1800 });//expires in 1800 seconds
        res.status(200).json(tk);
    }
});

Теперь в вашем интерфейсе поместите токен авторизации, отправленный этим маршрутом, в файлы cookie или хранить на стороне клиента. Выполните следующий запрос с авторизацией заголовка для безопасного маршрута панели мониторинга.

0 голосов
/ 15 мая 2020

Я думаю, проблема в функции контроллера входа

  • вы должны сначала проверить, правильный ли пароль у пользователя, прежде чем пытаться отправить ему токен

  • вы должны сохранить результат jwt sign function в переменной, чтобы отправить обратно пользователю, если у него есть правильные данные.

  • Нет смысла отправлять пароль еще раз пользователю, требуется только имя пользователя

вы можете попробовать это:


app.post('/', (req, res) => {

    const {username , password} = req.body;

    if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
        return res.json('Invalid credentials');
    }


    const token = jwt.sign({username:username }, SECRET)
    res.setHeader('Authorization', token);
    res.redirect('/dashboard')

});


Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...