Nodejs / mongodb-检查用户是否具有pipe理员权限(基于令牌的身份validation)
在我的express / mongoose应用程序中,我定义了verifyOrdinaryUser函数来检查用户是否在服务器上进行身份validation。 哪个效果很好,但是我已经在下面定义了verifyAdmin函数来检查用户是否也具有pipe理权限(我使用passport-local-mongoose模块来定义用户模式)。 正如你可以看到在verifyOrdinaryUser()函数中检查用户的令牌,它会加载一个名为解码的新属性的请求对象,我试图在verifyAdmin重用,这就是当我在postman中得到以下错误。
{ "message": "Cannot read property '_doc' of undefined", "error": {} }
以下是
var User = require('../models/user'); var jwt = require('jsonwebtoken'); var config = require('../config.js'); exports.getToken = function (user) { return jwt.sign(user, config.secretKey, { expiresIn: 3600 }); }; exports.verifyOrdinaryUser = function (req, res, next) { // check header or url parameters or post parameters for token var token = req.body.token || req.query.token || req.headers['x-access-token']; // decode token if (token) { // verifies secret and checks exp jwt.verify(token, config.secretKey, function (err, decoded) { if (err) { var err = new Error('You are not authenticated!'); err.status = 401; return next(err); } else { // if everything is good, save to request for use in other routes req.decoded = decoded; next(); } }); } else { // if there is no token // return an error var err = new Error('No token provided!'); err.status = 403; return next(err); } }; exports.verifyAdmin = function(req,res,next){ if(req.decoded._doc.admin !== true) { return next(err); }else { return next(); } };
我敢肯定,我已经在verifyAdmin函数中搞砸了一些东西。 中间件的顺序看起来是正确的我build议,欢迎
谢谢
编辑:中间件从这里app.js去
app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'jade'); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); // passport config var User = require('./models/user'); app.use(passport.initialize()); passport.use(new LocalStrategy(User.authenticate())); passport.serializeUser(User.serializeUser()); passport.deserializeUser(User.deserializeUser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', routes); app.use('/users', users); app.use('/dishes',dishRouter); app.use('/promotions',promoRouter); app.use('/leadership',leaderRouter);
我也陷入了同样的问题。 下面是verifyAdmin()的verify.js代码片段
exports.verifyAdmin = function(req, res, next){ // check header or url parameters or post parameters for token var token = req.body.token || req.query.token || req.headers['x-access-token']; // verifies secret and checks exp jwt.verify(token, config.secretKey, function (err, decoded) { if (err) { var err = new Error('You are not authenticated!'); err.status = 401; return next(err); } else { // They are an admin if (decoded._doc.admin){ return next(); } else { // They are not an admin var err = new Error('You are not authorized to perform this operation!'); err.status = 403; return next(err); } } }); };
我也在课堂上。 其他答案所提供的全面解释清楚地表明了对这些工具的深刻理解,但是我认为这些工作可能是过分的。 引用Jogesh教授在课程留言板上回答的问题:
“在你调用verifyAdmin之前,你确定你先调用了verifyOrdinaryUser吗?这两个必须被连接在一起。
从错误,它看起来像req.decoded不可用。 这意味着verifyOrdinaryUser没有被调用。 该function将解码的属性添加到请求中。
来自verify.js和app.js的代码看起来是正确的,不需要改变。 但是,在路由器中调用verifyAdmin函数时,请务必在调用verifyOrdinaryUser之后始终包含verifyAdmin,如下所示:
.get(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function(req, res, next) {
因为req.decode是在verifyOrdinaryUser中build立的,所以当你首先调用verifyAdmin而没有verifyOrdinaryUser时,你的解码仍然是未定义的。 您可以像其他答案所build议的那样使您的verifyAdminfunction更加彻底,但同样,这个任务不是必须的。
我正在做同样的Coursera课程,你是。 我刚完成这个任务! 你应该这样做: – 在你的verify.js文件中添加这个:
exports.verifyAdmin = function (req, res, next) { if (req.decoded._doc.admin == true) { next(); } else { // if the user is not admin // return an error var err = new Error('You are not authorized to perform this operation!'); err.status = 403; return next(err); } };
然后在你的路由器文件(如dishRouter.js)中,你应该使用这个中间件注入:
dishRouter.route('/') .get(Verify.verifyOrdinaryUser, function(req,res,next){ //res.end('Will send all the dishes to you!'); Dishes.find({}, function (err, dish) { if (err) throw err; res.json(dish); }); }) .post(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function(req, res, next){ //res.end('Will add the dish: ' + req.body.name + ' with details: ' + req.body.description); Dishes.create(req.body, function (err, dish) { if (err) throw err; console.log('Dish created!'); var id = dish._id; res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Added the dish with id: ' + id); }); }) .delete(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function(req, res, next){ //res.end('Deleting all dishes'); Dishes.remove({}, function (err, resp) { if (err) throw err; res.json(resp); }); });
你的代码看起来很好,只需要编辑verifyAdmin函数就可以了:
exports.verifyAdmin = function(req, res, next){ var isAdmin = req.decoded._doc.admin if (isAdmin) { return next(); } else { // if user is not admin // return an error var err = new Error ('You are not autorized to perform this operation!'); err.status = 403; return next(err); } }
在你的情况下,错误是因为variables“错误”没有在代码中定义。
问候。
对于那些可能不知道你的代码在做什么的人,我会给出一个长的解释,对于你的具体错误请滚动到这篇文章的底部
if (token) { // verifies secret and checks exp jwt.verify(token, config.secretKey, function (err, decoded) { if (err) { var err = new Error('You are not authenticated!'); err.status = 401; return next(err); } else { // if everything is good, save to request for use in other routes req.decoded = decoded; next(); }
将req.decoded属性设置为来自jwt.query的validation输出。 这将返回一个JSON对象,该对象具有来自authentication结果的有用信息。 除了validationencryption的密码jwt.query还检查用户有pipe理标志设置,这里是它返回的结果(在这种情况下发现真实)
decode.doc.admin字段是我们感兴趣的。 所以你可以使用类似的东西
jwt.verify(token, config.secretKey, function (err, decoded) { if (err) { var err = new Error('You are not authenticated!'); err.status = 401; return next(err); } else { // if everything is good, save to request for use in other routes if (decoded._doc.admin) { req.admin = true; } next(); } });
现在不需要对数据库进行进一步的查询,在下一个中间件中可以使用这个。 在你的情况下,你已经select发送整个解码对象,这也是没有错,但没有。 下一个中间件是你的function来validation是否从以前的结果pipe理员属性是真实的。
verifyAdmin = function(req,res,next){ if (req.admin) { console.log("Admin active"); next(); } else { var error = new Error('You do not have admin privileges!'); error.status = 401; return next(error) } };
您需要在路由器中一起使用这两个function
.post(verifyOrdinaryUser, verifyAdmin, function(req, res, next){
第一次validation会检查login,第二次validation会检查pipe理员。 结果在第一个函数本身中可用,您也可以在一个函数中完成它。
您的verifyAdmin代码是
exports.verifyAdmin = function(req,res,next){ if(req.decoded._doc.admin !== true) { return next(err); }else { return next(); } };
可能遭受 – 错误不可用,但你错误信息
{ "message": "Cannot read property '_doc' of undefined", "error": {} }
表示你没有req.decoded设置在首位。 在调用verifyOridinaryUser之前,最有可能调用了verifyAdmin。
req.decoded._doc.admin不工作
但是你可以使用这个function。
jwt.decode(token [,options]);
当然,例如,它适用于我:
exports.verifyAdmin = function(req, res, next) { // check header or url parameters or post parameters for token var token = req.body.token || req.query.token || req.headers['x-access-token']; // get the decoded payload and header "req.decoded._doc.admin" NOT WORKING var decAdmin = jwt.decode(token, { complete: true }); // decode token if (token) { // verifies secret and checks exp jwt.verify(token, config.secretKey, function(err, decoded) { if (err || !decAdmin.payload._doc.admin) { var err = new Error('You are not authorized to perform this operation!'); err.status = 403; return next(err); } else { // if everything is good, save to request for use in other routes req.decoded = decoded; next(); } }); } else { // if there is no token // return an error var err = new Error('No token provided!'); err.status = 403; return next(err); } };
从评论中,我猜你错误后app.use
ing verifyAdmin
是因为它被调用。 所以我们做了app.use(verifyAdmin())
而不是app.use(verifyAdmin)
。
请注意,通过调用这个没有任何参数, req
显然是不确定的。
我们想要的是Express来打电话。 我们只需要在某个地方填充这个函数。
中间件很有趣。
所以只是回顾一下,如果我正在跟踪这个权利,我认为我们得到了如下内容:
1)我们有一个app.js
(或者可能是index.js
或server.js
),它执行所有的app.use
路由器pipe道
2) app.js
已经有Passport设置,这是照顾authentication。
3)我们还有一个与以下中间件导出的模块:
-
verifyOrdinaryUser
-
verifyAdmin
我们来调用这个模块foo.js
4)我们想在我们只想login用户和pipe理员用户的地方使用verifyOrdinaryUser
和verifyAdmin
我会继续,并假设我们不希望用户能够得到任何地方,但登陆页面, app.use('/', routes);
,因此, 在这一行之后,我们添加app.use(verifyOrdinaryUser)
:
app.use('/', routes); // verifyOrdinaryUser will now be called before any middleware used AFTER this statement app.use(foo.verifyOrdinaryUser); app.use('/users', users); app.use('/dishes',dishRouter); app.use('/promotions',promoRouter); app.use('/leadership',leaderRouter);
现在让我们假设只有pipe理员可以访问/users
部分。 为此,我们将把verifyAdmin
中间件放在JUST users
路由器的前面。 现在我们的代码如下所示:
app.use('/', routes); // verifyOrdinaryUser will now be called before any middleware used AFTER this statement app.use(foo.verifyOrdinaryUser); // Call the verifyAdmin middleware BEFORE any middleware in the `users` router app.use('/users', foo.verifyAdmin, users); app.use('/dishes',dishRouter); app.use('/promotions',promoRouter); app.use('/leadership',leaderRouter);
这段代码做了很多假设,但是你应该能够适应它。
所有这一切说,req.decoded._doc似乎有点关头。 不应该护照处理verifyOrdinaryUser
用户部分?
我将不同的特权分配给不同的http请求,这是在… Router.js(promoRouter.js等)文件中完成的,这就是我不得不调用中间件的东西,如下所示
.get(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function(req,res,next){...}
所以在调用verifyOrdinaryUser之后,它会返回解码的请求,我可以使用的时候我会调用verifyAdmin。 我正在调用verifyAdmin,而没有首先检查用户是否被authentication,换句话说,首先调用verifyOrdinaryUser。
在相同的课程当然在哪里。
正如ForkInSpace所说,如果没有定义,那是因为你的代码没有通过第一个中间件
在第3周,我们没有使用.all(中间件)语法,并明确定义了每个操作的所有中间件
route('/') .get(Verify.verifyOrdinaryUser, function(req,res,next){...} .put(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function(req,res,next){...} .post(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function(req,res,next){...}
在第四周,教授介绍了简化代码的所有(中间件) 。
route('/') .all(Verify.verifyOrdinaryUser) .get(function(req,res,next){...} .put(Verify.verifyAdmin, function(req,res,next){...} .post( Verify.verifyAdmin, function(req,res,next){...}
但如果你像我一样,没有复制/粘贴课程中提供的代码,但是input它,你可能会错过这个。 并需要更新您的整个代码才能工作
使用console.log()
来查看解码实际是什么。 你会发现它是你的文档存储在一个“数据”对象。
那么你可以使用decoded.data.admin
来检查它。
编辑:
这是因为我的“获取令牌”看起来像这样。
exports.getToken = function (user) { return jwt.sign({**data:user**}, config.secretKey, { expiresIn: 3600 }); };
这个小小的改变会让事情变得非常简单。
你面对的问题是由于没有_doc
下的_doc
这个事实,正确的调用应该是按照下面的方式,
req.decoded.data.admin
为了确定我在说什么,只需在你的verifyAdmin
函数的某处添加console.log(req.decoded)
,你应该在你的控制台上find类似于以下的输出
MAC:passport username$ npm start ... Connected correctly to server { data: { _id: '59e011e9209e2613c5492b1d', salt: '...', hash: '...', username: 'username', __v: 0, admin: false }, iat: 1508012930, exp: 1508016530 } GET /dishes 401 18.242 ms - 70
因此,你的代码应该如下,
exports.verifyAdmin = function (req, res, next) { if (req.decoded.data.admin == true) { //NOTICE THE CHANGE "_doc --> data" next(); } else { // if the user is not admin // return an error var err = new Error('You are not authorized to perform this operation!'); err.status = 403; return next(err); } };
玩的开心 ;)
尝试这样做:
exports.verifyAdmin = function(req, res, next){ if(req.decoded._doc.admin){ return next(); }else{ var err = new Error('Not an Admin =.='); err.status = 401; return next(err); } };
由于verifyOrdinaryUser将被首先调用,req.decode将包含可用于validationadmin状态的已解码细节。
如果有人正在使用推荐的签名:
exports.jwtPassport = passport.use(new JwtStrategy(opts, (jwt_payload, done) => { console.log("JWT payload: ", jwt_payload); User.findOne({_id: jwt_payload._id}, (err, user) => { if (err) { return done(err, false); } else if (user) { return done(null, user); } else { return done(null, false); } }); })); exports. verifyOrdinaryUser = passport.authenticate('jwt', {session: false});
那么,您可能会注意到“用户”对象是从策略中返回的。 因此,通过设置:
exports.verifyAdmin = function(params, err, next) { if (params.user.admin){ return next(); } else { var err = new Error('Only administrators are authorized to perform this operation.'); err.status = 403; return next(err); } };
之后,在路线中的以下授权签名应该工作:
... .get(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function(req,res,next){...}
尽pipe如Emmanuel P.提到的那样,有一种更清洁的方式。
看着你的validationpipe理员代码,这是正确的,恰恰是“req.decoded._doc.admin”不再工作。 新的层次结构是“req.decoded.admin”。 要检查,并确定真正的层次结构,你可以console.log(req.decoded)。 👍🏽
那么,我刚刚开始这个任务 – 在牙科治疗后的周末发烧了 – 现在我已经坐了最后一个小时,试图把这个任务交给我 – 我在你们这个阶段 – 我想也许用robomongo编辑mongo db会重置令牌 – 这就是导致我的validationpipe理员失败的原因 – 无论如何,我想 – 我正在rest,然后再继续
- 我如何使用node.js来刮取需要validation的网站?
- REST API和移动应用程序的身份validation策略
- 改进node.js代码(后缀,承诺和护照)
- 如何在Node.js中重新使用由Play Framework 2.2设置的签名cookie?
- 请求反馈:使用客户端复制(pouchdb)为多个用户/帐户设置Couchdb
- Passport.js + Express.jsauthentication后将用户转发到原来的目的地
- 节点JS LDAPauthentication用户
- node.js oauth2服务器到服务器客户端(谷歌跟踪authentication)
- Node.js Passport在页面加载上获得Facebook会话