能夠參考這篇總結 開發安全的 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開發上有所啓發,小生才疏學淺,過程當中有什麼不足的地方也歡迎指正。