快速路由和jsonwebtoken,在创建令牌后保持登录状态 [英] Express routing and jsonwebtoken, staying logged in after token creation
问题描述
我很难连接最后一个点,从而在Express中构建基于角色的访问控制api.遵循本教程并实施迁移到我现有的程序上,但是我想我错过了最后一步,并且在无数的教程分析陷入瘫痪之后.我将所有必要的代码缩减到我认为是最低限度的水平.
I'm having a hard time connecting the last dots building a role based access control api in Express. Following this tutorial and implementing onto my existing program, but I think I am missing the last step and after countless tutorials analysis paralysis has set in. I have since scaled back all my necessary code to what I think is the bare minimum.
当前,我能够创建一个新用户并将其保存到mongoose数据库中.我可以看到bcrypt的哈希正在完成它的工作,并且可以看到注册后响应中正在生成令牌.但是,一旦我在注册或登录后导航到新页面(例如,根据教程,用户拥有自己的id页面/user/:userId
),我就会不断收到您需要登录
.我知道我需要在每个请求中检查一个令牌,但是我的问题是,为什么中间件似乎不检查令牌还是有东西阻止它?
Currently I am able to create a new user and save them to the mongoose database. I can see the hash by bcrypt is doing its thing and I can see the token being generated in the response after signing up. However as soon as I navigate to a new page after signup or login, for eg the users own id page/user/:userId
as per tutorial, I keep getting You need to be logged in
. I know I need to check for a token on every request but my question is, why doesn't it seem like the middleware is checking for the token or something is holding it back?
由于令牌肯定显示在json响应中,因此我应该能够通过例如/user/:userId
页面的下一个get请求来检查令牌是否存在?那不是主意吗?还是浏览器只是显示响应,但我仍然需要实际存储它?我不知道该怎么说..
Since the token is shown in the json reponse surely I should be able to check for the tokens existence with the next get request at for eg the /user/:userId
page? Isn't that the idea? Or is the browser just showing the response but I still need to actually store it? I don't understand where it goes to so to speak..
有什么建议吗?还是这是会议的事情?我不知道所有代码会有点困难,但是如果有人能发现任何相关的内容,以便我研究下一步,我将不胜感激!
Any advice? Or is this a session thing? I know its a bit hard without all the code but if anyone could spot anything relevant so that I could research my next steps I would much appreciate it!
首先在app.js中使用该中间件
First this middleware in app.js
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.use('/', async (req, res, next) => {
if (req.headers['x-access-token']) {
try {
const accessToken = req.headers['x-access-token'];
const {userId, exp} = await jwt.verify(accessToken, process.env.JWT_SECRET);
console.log('token verified'); // not printing to console
// If token has expired
if (exp < Date.now().valueOf() / 1000) {
return res.status(401).json({
error: 'JWT token has expired, please login to obtain a new one',
});
}
res.locals.loggedInUser = await User.findById(userId);
next();
} catch (error) {
next(error);
}
} else {
next();
}
});
app.use('/',userRoutes);
app.use('/', userRoutes);
我已经使用必需的模块 access-control
构建了角色
I have built the roles using the module access-control
which is required
const AccessControl = require('accesscontrol');
const ac = new AccessControl();
exports.roles = (function() {
ac.grant('basic')
.readOwn('profile')
.updateOwn('profile');
ac.grant('supervisor')
.extend('basic')
.readAny('profile');
ac.grant('admin')
.extend('basic')
.extend('supervisor')
.updateAny('profile')
.deleteAny('profile');
return ac;
})();
按照教程路由示例.
router.get('/signup', (req, res, next) => {
res.render('signup', {
viewTitle: 'User SignUp',
});
});
router.post('/signup', userController.signup);
router.get('/login', (req, res, next) => {
res.render('login', {
viewTitle: 'User Login - WTCT OPS',
});
});
router.post('/login', userController.login );
router.get('/add', userController.allowIfLoggedin, userController.grantAccess('readAny', 'profile'), userController.add);
router.get('/users', userController.allowIfLoggedin, userController.grantAccess('readAny', 'profile'), userController.getUsers);
router.get('/user/:userId', userController.allowIfLoggedin, userController.getUser);
router.put('/user/:userId', userController.allowIfLoggedin, userController.grantAccess('updateAny', 'profile'), userController.updateUser);
router.delete('/user/:userId', userController.allowIfLoggedin, userController.grantAccess('deleteAny', 'profile'), userController.deleteUser);
控制器的相关部分
async function hashPassword(password) {
return await bcrypt.hash(password, 10);
}
async function validatePassword(plainPassword, hashedPassword) {
return await bcrypt.compare(plainPassword, hashedPassword);
}
// grant access depending on useraccess role
exports.grantAccess = function(action, resource) {
return async (req, res, next) => {
try {
const permission = roles.can(req.user.role)[action](resource);
if (!permission.granted) {
return res.status(401).json({
error: 'You don\'t have enough permission to perform this action',
});
}
next();
} catch (error) {
next(error);
}
};
};
// allow actions if logged in
exports.allowIfLoggedin = async (req, res, next) => {
try {
const user = res.locals.loggedInUser;
if (!user) {
return res.status(401).json({
error: 'You need to be logged in to access this route',
});
}
req.user = user;
next();
} catch (error) {
next(error);
}
};
// sign up
exports.signup = async (req, res, next) => {
try {
const {role, email, password} = req.body;
const hashedPassword = await hashPassword(password);
const newUser = new User({email, password: hashedPassword, role: role || 'basic'});
const accessToken = jwt.sign({userId: newUser._id}, process.env.JWT_SECRET, {
expiresIn: '1d',
});
newUser.accessToken = accessToken;
await newUser.save();
res.send({
data: newUser,
message: 'You have signed up successfully',
});
} catch (error) {
next(error);
}
};
exports.login = async (req, res, next) => {
try {
const {email, password} = req.body;
const user = await User.findOne({email});
if (!user) return next(new Error('Email does not exist'));
const validPassword = await validatePassword(password, user.password);
if (!validPassword) return next(new Error('Password is not correct'));
const accessToken = jwt.sign({userId: user._id}, process.env.JWT_SECRET, {
expiresIn: '1d',
});
await User.findByIdAndUpdate(user._id, {accessToken});
res.status(200).json({
data: {email: user.email, role: user.role},
accessToken,
});
} catch (error) {
next(error);
}
};
// get one user
exports.getUser = async (req, res, next) => {
try {
const userId = req.params.userId;
const user = await User.findById(userId);
if (!user) return next(new Error('User does not exist'));
// console.log(req.params);
res.send(200).json({
data: user,
});
} catch (error) {
next(error);
}
};
为什么在尝试发布到端点/user/:userId
时,中间件不检查令牌?
Why when trying to post to the endpoint /user/:userId
is the middleware not checking for the token?
谢谢您的任何建议!
更新:
到目前为止,我已经尝试从app.use中删除/
.我看到我现在犯了这个错误,但是还尝试从 app.use(userRoutes);
中间件中删除它,使它适用于所有http请求,但没有运气.
So far I have tried to removed the /
from app.use. I saw I made that mistake now, but also tried removing it from the app.use(userRoutes);
middleware to make it apply to all http requests but no luck.
app.use(async (req, res, next) => {
if (req.headers['x-access-token']) {
try {
const accessToken = req.headers['x-access-token'];
const {userId, exp} = await jwt.verify(accessToken, process.env.JWT_SECRET);
// If token has expired
if (exp < Date.now().valueOf() / 1000) {
return res.status(401).json({
error: 'JWT token has expired, please login to obtain a new one',
});
}
res.locals.loggedInUser = await User.findById(userId);
// console.log('Time:', Date.now());
next();
} catch (error) {
next(error);
}
} else {
next();
}
});
app.use(userRoutes);
我还认为这可能是因为我的服务器在后端发出了http请求,也许是在设置 x-access-token
标头时出现了问题?因此,我尝试将 x-access-token
mw更改为在所有路由上都使用 router.use
,但仍然一无所获.我不明白我在想什么.并且只是为了确保我没有遗漏一些基本知识,因为我使用的是JWT,所以我无需使用本地存储或cookie即可在登录时在页面之间进行浏览,因为我可以使用标头中设置的令牌,?
I also thought that maybe because my server makes http requests in the backend maybe that was causing a problem in setting the x-access-token
header? So I tried to change the x-access-token
mw to use router.use
on all routes but still nothing. I don't understand what I am missing. And just to be sure I'm not missing something fundamental, since I am using the JWT I do not need to use local storage or cookies to allow for browsing between pages while logged in since I can use the token set in the header, correct?
再次感谢您的任何建议!
Thanks again for any advice!
推荐答案
这是因为您的中间件仅绑定到/
路由.如果您希望将其用于所有路线,请将其删除.看看ExpressJS 关于中间件的文档.
That's because your middleware is only tied to the /
route. Remove it if you want it to be used for every route. Take a look at the ExpressJS Docs regarding middleware.
这篇关于快速路由和jsonwebtoken,在创建令牌后保持登录状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!