能够参考这篇总结 开发安全的 API 所须要核对的清单javascript
git clone https://github.com/Nicksapp/nAuth-restful-api.git
前端
npm install
java
具体数据库配置信息在config.js中设置node
开发前先进行咱们设计的构想git
路由设计github
user 模型设计web
网上已经有了不少关于RESTful的介绍,我这里也不过多重复了。想说的就是它的主要做用,就是对于现现在的网络应用程序,分为前端和后端两个部分,然而当前的发展趋势就是应用平台需求的扩大(IOS、Android、Webapp等等)mongodb
所以,就须要一种统一的机制,方便不一样的应用平台的前端设备与后端进行通讯,也就是先后端的分离。这致使了API架构的流行,甚至出现"API First"的设计思想。RESTful API则是目前比较成熟的一套互联网应用程序的API设计理论。数据库
使用Node.js上的Express框架进行咱们的路由设计,Mongoose来与Mongodb数据库链接交互,使用Postman对咱们设计的Api进行调试,快动起手来吧!express
在API设计中,TOKEN用来判断用户是否有权限访问API.TOKEN首先不须要编解码处理. 通常TOKEN都是一些用户名+时间等内容的MD5的不可逆加密.而后经过一个USER_TOKEN表来判断用户请求中包含的TOKEN与USER_TOKEN表中的TOKEN是否一致便可.
具体实践过程主要为:
废话很少说,先进入看咱们的干货,此次选用Node.js+experss配合Mongoose来进入REST的token实践
项目地址: GitHub地址
或 git clone https://github.com/Nicksapp/nAuth-restful-api.git
先看看咱们的项目文件夹
- routes/ ---- index.js ---- users.js - models/ ---- user.js - config.js - package.json - passport.js - index.js
npm init
建立咱们的package.json
接着在项目根文件夹下安装咱们所需的依赖
npm install express body-parser morgan mongoose jsonwebtoken bcrypt passport passport-http-bearer --save
-- save会将咱们安装的库文件写入package.json
的依赖中,以便其余人打开项目是可以正确安装所需依赖.
定义咱们所需用户模型,用于moogoose,新建models/user.js
const mongoose = require('mongoose'); const Schema = mongoose.Schema; const bcrypt = require('bcrypt'); const UserSchema = new Schema({ name: { type: String, unique: true, // 不可重复约束 require: true // 不可为空约束 }, password: { type: String, require: true }, token: { type: String } }); // 添加用户保存时中间件对password进行bcrypt加密,这样保证用户密码只有用户本人知道 UserSchema.pre('save', function (next) { var user = this; if (this.isModified('password') || this.isNew) { bcrypt.genSalt(10, function (err, salt) { if (err) { return next(err); } bcrypt.hash(user.password, salt, function (err, hash) { if (err) { return next(err); } user.password = hash; next(); }); }); } else { return next(); } }); // 校验用户输入密码是否正确 UserSchema.methods.comparePassword = function(passw, cb) { bcrypt.compare(passw, this.password, (err, isMatch) => { if (err) { return cb(err); } cb(null, isMatch); }); }; module.exports = mongoose.model('User', UserSchema);
./config.js
用来配置咱们的MongoDB数据库链接和token的密钥。
module.exports = { 'secret': 'learnRestApiwithNickjs', // used when we create and verify JSON Web Tokens 'database': 'mongodb://localhost:27017/test' // 填写本地本身 mongodb 链接地址,xxx为数据表名 };
./index.js
服务器配置文件,也是程序的入口。
这里咱们主要用来包含咱们程序须要加载的库文件,调用初始化程序所须要的依赖。
const express = require('express'); const app = express(); const bodyParser = require('body-parser');// 解析body字段模块 const morgan = require('morgan'); // 命令行log显示 const mongoose = require('mongoose'); const passport = require('passport');// 用户认证模块passport const Strategy = require('passport-http-bearer').Strategy;// token验证模块 const routes = require('./routes'); const config = require('./config'); let port = process.env.PORT || 8080; app.use(passport.initialize());// 初始化passport模块 app.use(morgan('dev'));// 命令行中显示程序运行日志,便于bug调试 app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); // 调用bodyParser模块以便程序正确解析body传入值 routes(app); // 路由引入 mongoose.Promise = global.Promise; mongoose.connect(config.database); // 链接数据库 app.listen(port, () => { console.log('listening on port : ' + port); })
./routes
主要存放路由相关文件
./routes/index.js
路由总入口,引入所使用路由
module.exports = (app) => { app.get('/', (req, res) => { res.json({ message: 'hello index!'}); }); app.use('/api', require('./users')); // 在全部users路由前加/api };
./routes/users.js
const express = require('express'); const User = require('../models/user'); const jwt = require('jsonwebtoken'); const config = require('../config'); const passport = require('passport'); const router = express.Router(); require('../passport')(passport); // 注册帐户 router.post('/signup', (req, res) => { if (!req.body.name || !req.body.password) { res.json({success: false, message: '请输入您的帐号密码.'}); } else { var newUser = new User({ // 在库中建立一个新用户 name: req.body.name, password: req.body.password }); // 保存用户帐号 newUser.save((err) => { if (err) { return res.json({success: false, message: '注册失败!'}); } res.json({success: true, message: '成功建立新用户!'}); }); } }); // 检查用户名与密码并生成一个accesstoken若是验证经过 router.post('/user/accesstoken', (req, res) => { User.findOne({ // 根据用户名查找是否存在该用户 name: req.body.name }, (err, user) => { if (err) { throw err; } if (!user) { res.json({success: false, message:'认证失败,用户不存在!'}); } else if(user) { // 检查密码是否正确 user.comparePassword(req.body.password, (err, isMatch) => { if (isMatch && !err) { var token = jwt.sign({name: user.name}, config.secret,{ expiresIn: 10080 // token 过时销毁时间设置 }); user.token = token; user.save(function(err){ if (err) { res.send(err); } }); res.json({ success: true, message: '验证成功!', token: 'Bearer ' + token, name: user.name }); } else { res.send({success: false, message: '认证失败,密码错误!'}); } }); } }); }); // passport-http-bearer token 中间件验证 // 经过 header 发送 Authorization -> Bearer + token // 或者经过 ?access_token = token router.get('/user/user_info', passport.authenticate('bearer', { session: false }), function(req, res) { res.json({username: req.user.name}); }); module.exports = router;
./passport.js
配置权限模块所需功能
const passport = require('passport'); const Strategy = require('passport-http-bearer').Strategy; const User = require('./models/user'); const config = require('./config'); module.exports = function(passport) { passport.use(new Strategy( function(token, done) { User.findOne({ token: token }, function(err, user) { if (err) { return done(err); } if (!user) { return done(null, false); } return done(null, user); }); } )); };
主要验证发送的token值与用户服务器端token值是否匹配,进行信息验证。
如今就能够运行咱们的代码看具体运做过程了!为了便于调试与参数的收发,咱们使用postman(可在Chrome上或Mac上安装)来操做.
node index
运行咱们的本地服务器,访问 [localhost:8080/]()
应该就能够看到咱们所返回的初始json值了,然咱们继续深刻测试。
POST访问[localhost:8080/api/signup](),咱们来注册一个新用户,注意要设置body
的Content-Type
为x-www-form-urlencoded
以便咱们的body-parser
可以正确解析,好的咱们成功模拟建立了咱们的新用户。
链接一下数据库看下咱们的用户信息是否也被正确存储(注:我使用的是MongoChef,十分强大MongoDB数据库管理软件),咱们能够看到,个人password也被正确加密保存了。
接着POST访问[localhost:8080/api/user/accesstoken](),来为个人用户得到专属token,POST过程与注册相关,能够看到也正确生成咱们的token值。
再看下咱们的数据库中的用户信息,token值也被存入了进来,便于咱们以后进行权限验证。
GET访问[localhost:8080/api/user/user_info](),同时将咱们的token值在Header
中以 Authorization: token
传入,正确得到用户名则表示咱们访问请求经过了验证。
若是token值不正确,则返回HTTP状态码 401 Unauthorized 并拒绝访问请求。到这里咱们的权限验证功能也就基本实现了。
但愿在看完这篇教程后可以对你在RESTful Api开发上有所启发,小生才疏学浅,过程当中有什么不足的地方也欢迎指正。