如何testing使用JWTauthentication的Node API(用户login获取令牌)
TL; DR – testing使用JWT进行身份validation的Node API(Express)中的资源的方法是什么?凭证本身只授予用户名/密码login名?
我有点新的testing,并希望得到一些build议。 最终目标是要有一个经过全面testing的API,然后开始学习如何把这个连接到一个持续集成解决scheme。
正在使用的技术
- 我使用Express在Node中编写了一个API。
- Mongo是数据库。
- mongoose被用作ODM。
- jsonwebtoken包用于创build/validation令牌。
- Passport用于在路由上轻松地将用户身份validation添加为Express中间件。
API信息
API有各种资源 – 其中的具体细节对于这个查询并不重要,但为了简单起见,我们只是假装它是无处不在的Todo应用程序。
保存在数据库中的每个单独的资源都与一个用户相关联。
API使用JWT跨各种资源端点进行身份validation。 令牌本身包含唯一的用户ID,该ID是针对Mongo数据库中的资源存储的。 要获取令牌本身需要用户首先注册(返回令牌),然后login以获取新的令牌。
假装代码。
我将简化下面的代码,而不是使用任何环境configuration等等…
app.js
var express = require('express'); var app = express(); var mongoose = require('mongoose'); var bodyParser = require('body-parser'); var passport = require('passport'); mongoose.connect('mongodb://localhost/somedatabasename'); app.set('port', process.env.PORT || 3000); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); app.use(passport.initialize()); // ... Passport JWT Strategy goes here - omitted for simplicity ... var userRouter = require('./api/users/routes'); app.use('/users', userRouter); var todoRouter = require('./api/todos/routes'); app.use('/todos', todoRouter); app.listen(app.get('port'), function() { console.log('App now running on http://localhost:' + app.get('port')); });
./api/todos/routes.js
var router = require('express').Router(); var controller = require('./controller'); var passport = require('passport'); router.route('/') .all(passport.authenticate('jwt', { session: false})) .get(controller.getAll) .post(controller.create); router.route('/:id') .all(passport.authenticate('jwt', { session: false})) .get(controller.getOne) .put(controller.update) .delete(controller.delete); module.exports = router;
./api/users/routes.js
var router = require('express').Router(); var controller = require('./controller'); var passport = require('passport'); router.route('/') // User signup .post(controller.create); router.route('/me') // User Login .post(passport.authenticate('local', { session: false}), controller.login) // Get current user's data .get(passport.authenticate('jwt', { session: false}), controller.getOne) // Update current user's data .put(passport.authenticate('jwt', { session: false}), controller.update) // Delete current user .delete(passport.authenticate('jwt', { session: false}), controller.delete); module.exports = router;
./api/users/model.js
var mongoose = require('mongoose'); var bcrypt = require('bcrypt'); var UserSchema = new mongoose.Schema({ username: { type: String, required: true, unique: true }, password: { type: String, required: true } }); // ... for simplicity imagine methods here to // - hash passwords on a pre save hook using bcrypt // - compare passwords using bcrypt when logging in module.exports = mongoose.model('User', UserSchema);
./api/todos/model.js
var mongoose = require('mongoose'); var Schema = mongoose.Schema; var momentSchema = new Schema({ title: { type: String }, // Bunch of other fields here... _user: { type: Schema.Types.ObjectId, ref: 'User' } }); module.exports = mongoose.model('Moment', momentSchema);
我已经省略了一些示例代码来保持简洁。
例如, 用户的控制器将包括模型及其function将:
- controller.create – 注册新用户(返回令牌)
- controller.login – 在通过Passport Local确认的用户名/密码组合之后,返回一个有效的标记
- controller.getOne – 基于从JWT token获取的User ID,使用Mongoose从Mongo返回用户的数据。
- controller.update – 使用Mongoose更新Mongo中的用户数据
- controller.delete – 使用Mongoose删除Mongo中的用户数据
Todo的控制器会做类似的事情 – 只是通过Mongoose与Mongo数据交互,但是查询总是包含用户ID,以将特定的(例如)Todo项目与经过authentication的用户(通过JWT进行身份validation)相关联。
testing难题
如何使用Mocha,Chai和SuperTest的组合来testing这样的testing?
我可以吗:
- 在Mongo中创build一个testing数据库,并在testing中使连接string不同? 这意味着要保存数据库中存储的实际数据以供testing。
- 以某种方式模拟数据,而不是使用testing数据库? 但是,如何处理用户保存/login来检索令牌?
在开发过程中如何进行本地testing,或者使用某种CI工具进行部署时(我在学习中还没有达到的东西)?
任何援助将不胜感激,我希望我已经提供了足够的信息与上面的虚拟数据/代码:/
在testing过程中,你通常会嘲笑你的mongo数据库(类似于mongo-mock
。这样,你不需要运行实际的数据库来运行你的testing(你不是testing数据库,而是你的代码)。
在testing过程中,您将用mongo-mock
replacemongodb
,然后运行testing。 要得到您的令牌,您需要使用有效的模拟凭证发布到您的/me
url,该terminal将返回令牌,然后在您的下一个呼叫中使用该令牌来testing您的其他terminal。
在事物的令牌方面,我通常在进入其他端点之前在请求开始时检查它。 (我没有使用护照,但是这个想法是):
app.use(validate_jwt_middleware); app.use('/users', userRouter);
这样,如果令牌无效,则整个网站,而不是您的部分是无效的。
另外,我并没有使用SuperTest,而是使用chai-http,所以我无法帮助您解决具体问题。
希望这可以帮助,