如何将 jwt 令牌发送到 node.js 中的受保护路由
Posted
技术标签:
【中文标题】如何将 jwt 令牌发送到 node.js 中的受保护路由【英文标题】:How to send jwt token to protected route in 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);
【问题讨论】:
这可能有助于***.com/questions/59241078/… 用户登录时如何将令牌发送到服务器? 您希望令牌位于标头中,但您在前端 javascript 中执行任何操作以在标头中发送接收到的令牌? 我错误地上传了旧代码,我现在更新了我的问题,当用户密码在 else 语句中匹配时,我尝试发送它 res.setHeader('Authorization', 'Bearer'+ token) ; 您能否也发布您的前端代码,从您将 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')
);
【讨论】:
以上是关于如何将 jwt 令牌发送到 node.js 中的受保护路由的主要内容,如果未能解决你的问题,请参考以下文章
Node.js:登录后在 URL 中发送带有令牌(JWT)的新请求
如何从 Node.js 中的 jwt 令牌获取 user.id?