【问题标题】:How to send jwt token to protected route in node.js如何将 jwt 令牌发送到 node.js 中的受保护路由
【发布时间】:2020-08-21 15:04:33
【问题描述】:

我创建了一个登录表单,如果用户输入正确的密码和用户名,该表单应将用户重定向到仪表板页面。如果用户在未登录的情况下尝试导航到仪表板 URL,则不应显示该页面,因为它是受保护的路由。我试图在用户登录时发送 jwt 令牌,但这不起作用我登录时只收到禁止消息,所以似乎令牌未正确发送,我该如何发送 jwt 令牌并访问用户登录成功后的受保护路由?

这是我的 server.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);

【问题讨论】:

  • 用户登录时如何将令牌发送到服务器?
  • 您希望令牌位于标头中,但您在前端 Javascript 中执行任何操作以在标头中发送接收到的令牌?
  • 我错误地上传了旧代码,我现在更新了我的问题,当用户密码在 else 语句中匹配时,我尝试发送它 res.setHeader('Authorization', 'Bearer'+ token) ;
  • 您能否也发布您的前端代码,从您将 jwt 发送到后端的位置

标签: node.js express jwt


【解决方案1】:

看这个例子,我使用中间件(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/mongoose.js:

const mongoose = require('mongoose');

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

路由器/user.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-user.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};

【讨论】:

    【解决方案2】:

    您的问题是您的token 变量只能在jwt.sign 调用的回调内部访问,所以当您尝试在res.setHeader('Authorization', 'Bearer '+ token); 执行此操作时,它不会知道您指的是哪个变量,因此出现未定义的错误。顺便说一句,如果您要异步使用jwt.sign,那么使用它的代码也需要在回调内部,否则回调外部的同步代码可能会首先执行(因此无法访问异步代码的任何结果),因为异步回调在后台执行。这里的解决方案是将您的使用切换为同步使用,或者将您的响应代码放在回调中。此外,调用res.json 将结束响应,所以我不确定您究竟想通过多个响应调用来完成什么

    同步版本:

    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(当使用不包括加密的默认/标准算法时)在加密上保证是不可修改的,但它们仍然可读 em>

    【讨论】:

      【解决方案3】:

      我有一种发送 jwt 令牌的解决方案,但您需要再安装一个包。如果您认为值得,也许您可​​以关注。

      我只将 express 用于后端 api。但您可以将此处应用的相同逻辑应用于您的应用程序。

      您需要安装的库是express-jwt

      它处理路由以阻止对需要身份验证的端点的访问。

      server.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 中或存储在客户端中。 使用安全仪表板路由的标头授权执行下一个请求。

      【讨论】:

        【解决方案4】:

        我认为是登录控制器功能的问题

        • 在尝试向他发送令牌之前,您必须先检查用户是否拥有正确的密码

        • 您应该将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')
        
        });
        
        
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-10-18
          • 2020-12-14
          • 2021-03-10
          • 2020-03-25
          • 2019-11-22
          • 1970-01-01
          • 2018-09-12
          • 2020-10-01
          相关资源
          最近更新 更多