在我的express / mongoose应用程序中,我正在定义verifyOrdinaryUser函数来检查用户是否在服务器上进行了身份验证。哪个效果很好,但是我在下面定义了verifyAdmin函数以检查用户是否也具有管理员权限(我使用passport-local-mongoose模块来定义用户模式)。 正如您所看到的,在verifyOrdinaryUser()函数中检查了用户的令牌,它会将一个名为decoding的新属性加载到我正在尝试在verifyAdmin中重用的请求对象,那就是我在邮递员中收到以下错误的时候。
{
"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函数中弄乱了一些东西。 中间件订单看起来对我来说正确 欢迎提出建议
谢谢
编辑:中间件来自app.jsapp.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);
答案 0 :(得分:7)
我也在课堂上。其他答案提供的详尽解释清楚地表明了对这些工具的深刻理解,但我认为它们对于这项任务可能有点过分。将Jogesh教授在课程留言板上的回复引用到具有类似问题的主题:
"您是否确定在调用verifyAdmin之前首先调用了verifyOrdinaryUser?这两者必须一个接一个地链接。
从错误中看,req.decoded似乎不可用。这意味着未调用verifyOrdinaryUser。此函数将已解码的属性添加到req。"
来自verify.js和app.js的代码看起来正确,不需要更改。但是,在路由器中调用verifyAdmin函数时,请确保在调用verifyOrdinaryUser之后始终包含verifyAdmin,如下所示:
.get(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function(req, res, next) {
因为req.decode是在verifyOrdinaryUser中建立的,所以当您首先调用verifyAdmin而不使用verifyOrdinaryUser时,您的解码仍未定义。您可以像其他答案建议的那样使您的verifyAdmin功能更加彻底,但同样,此分配也不是必需的。
答案 1 :(得分:5)
我也遇到了同样的问题。以下是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);
}
}
});
};
答案 2 :(得分:3)
我正在做同样的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);
});
});
答案 3 :(得分:2)
req.decoded._doc.admin 不工作
但你可以使用这个功能。
课程示例,它适用于我:
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);
}
};
答案 4 :(得分:2)
您的代码看起来很好,只需编辑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);
}
}
在您的情况下,错误是因为代码中未定义变量“error”。
问候。
答案 5 :(得分:2)
为了那些可能无法理解您的代码正在做什么的人的利益,我会给出一个很长的解释,对于您的具体错误,请滚动到这篇文章的底部
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的验证输出。这将返回一个JSON对象,该对象具有来自身份验证结果的有用信息。除了身份验证之外的jwt.query加密密码还检查用户是否设置了admin标志,这里是它返回的结果(在这种情况下发现为true)
我们感兴趣的是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();
}
});
现在无需对数据库进行进一步查询,在下一个中间件中您可以使用它。在你的情况下,你已经选择发送整个解码的对象,这也没有错,但不必要。下一个中间件是您的函数,用于验证以前的结果中admin属性是否为true。
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)
}
};
您需要在路由器中一起使用这两个功能
.post(verifyOrdinaryUser, verifyAdmin, function(req, res, next){
第一次验证将检查登录,第二次验证将检查管理员。结果可在第一个函数本身中获得,您也可以在一个函数中完成它。
您的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。
答案 6 :(得分:1)
从评论中,我猜你在app.use
之后得到的错误
verifyAdmin
是因为它被调用了。我们做了app.use(verifyAdmin())
而不是app.use(verifyAdmin)
。
请注意,通过不带任何参数调用此内容,req
显然会undefined
。
我们想要的是 Express 来进行调用。我们只需要在某个地方填充这个功能。
中间件很有趣。
所以回顾一下,如果我追踪到这一点,我认为我们得到的是以下内容:
我们有一个app.js
(或者index.js
或server.js
)来完成所有app.use
路由器管道
app.js
已设置Passport,负责身份验证。
我们还有一个模块,其中导出了以下中间件:
verifyOrdinaryUser
verifyAdmin
我们将此模块称为foo.js
。
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);
现在让我们假设只有管理员可以访问/users
部分。为此,我们将verifyAdmin
中间件放在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的东西一开始似乎有些偏差。 Passport不应该处理verifyOrdinaryUser
用户部分吗?
答案 7 :(得分:1)
我正在为不同的http请求分配不同的权限,这是在... Router.js(promoRouter.js等)文件中完成的,我必须将中间件称为以下内容
.get(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function(req,res,next){...}
所以在调用verifyOrdinaryUser后,它会返回请求中的解码,我可以在调用verifyAdmin时使用。我在调用verifyAdmin时没有先检查用户是否经过身份验证,换句话说就是先调用verifyOrdinaryUser。
答案 8 :(得分:1)
同一课程在哪里。
正如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){...}
在第4周,教授介绍了 .all(中间件),简化了代码。
route('/')
.all(Verify.verifyOrdinaryUser)
.get(function(req,res,next){...}
.put(Verify.verifyAdmin, function(req,res,next){...}
.post( Verify.verifyAdmin, function(req,res,next){...}
但是如果你像我一样,没有复制/粘贴课程中提供的代码,但输入它,你可能会错过这个。并需要更新您的整个代码才能工作
答案 9 :(得分:1)
使用console.log()
查看实际解码的内容。您会发现它是存储在“数据”对象中的文档。
然后您可以使用decoded.data.admin
进行检查。
编辑:
这是因为我的“获取令牌”看起来像这样。
exports.getToken = function (user) {
return jwt.sign({**data:user**}, config.secretKey, {
expiresIn: 3600
});
};
这一小改动将使事情变得非常简单。
答案 10 :(得分:1)
您遇到的问题是由于_doc
下没有decoded
,正确的呼叫应按照以下方式进行,
req.decoded.data.admin
要确定我在说什么,只需在console.log(req.decoded)
函数中的某处添加verifyAdmin
,您就应该在控制台上找到类似于以下内容的输出
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);
}
};
玩得开心;)
答案 11 :(得分:1)
exports.verifyAdmin = ((req,res,next) =>{
const name = req.body.username
console.log(name)
User.findOne({username: name},(err,user) => {
if(err) {
next(err)
}
else if(!user) {
next(new Error("user not found"))
}
else {
if(!user.admin) {
next(new Error("you are not an admin"))
}
else {
next()
}
}
});
});
在我的情况下有效。将用户确认为普通用户后即可使用此功能。
答案 12 :(得分:0)
尝试这样做:
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将包含已解码的详细信息,可用于验证admin的状态。
答案 13 :(得分:0)
如果有人使用推荐的签名:
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});
然后,您可能会注意到该策略返回了“user”对象。因此,通过设置:
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){...}
尽管正如Emmanuel P.所说,但有一种更清洁的方式。
答案 14 :(得分:0)
查看您的验证管理员代码,它是正确的恰好是" req.decoded._doc.admin"不再工作了。新的层次结构是" req.decoded.admin"。要检查并确定真正的层次结构,您可以使用console.log(req.decoded)。
答案 15 :(得分:-1)
好吧,我刚刚开始任务 - 在获得牙科治疗后的周末发烧了 - 现在我已经坐了最后一个小时并试图将任务调整到位 - 我在舞台上我们你们都是 - 我想也许用robomongo编辑mongo db会重置令牌 - 导致我的验证管理员失败的原因 - 无论如何我想 - 我在继续之前休息一下