摘要:則是目前比較成熟的一套互聯網應用程序的設計理論。則允許操作,不一樣,報錯返回或者加入黑名單。再看下我們的數據庫中的用戶信息,值也被存入了進來,便于我們之后進行權限驗證。訪問同時將我們的值在中以傳入正確獲得用戶名則表示我們訪問請求通過了驗證。
關于安全性方面的建議
可以參考這篇總結 開發安全的 API 所需要核對的清單
安裝git clone https://github.com/Nicksapp/nAuth-restful-api.git
運行npm install
具體數據庫配置信息在config.js中設置
整體構架開發前先進行我們設計的構想
路由設計
POST /api/signup: 用戶注冊
POST /api/user/accesstoken: 賬號驗證,獲取token
GET /api/users/info: 獲得用戶信息,需驗證
user 模型設計
name : 用戶名
password: 密碼
token: 驗證相關token
關于RESTful API網上已經有了很多關于RESTful的介紹,我這里也不過多重復了。想說的就是它的主要作用,就是對于現如今的網絡應用程序,分為前端和后端兩個部分,然而當前的發展趨勢就是應用平臺需求的擴大(IOS、Android、Webapp等等)
因此,就需要一種統一的機制,方便不同的應用平臺的前端設備與后端進行通信,也就是前后端的分離。這導致了API架構的流行,甚至出現"API First"的設計思想。RESTful API則是目前比較成熟的一套互聯網應用程序的API設計理論。
技術棧使用Node.js上的Express框架進行我們的路由設計,Mongoose來與Mongodb數據庫連接交互,使用Postman對我們設計的Api進行調試,快動起手來吧!
API設計中的token的思路在API設計中,TOKEN用來判斷用戶是否有權限訪問API.TOKEN首先不需要編解碼處理. 一般TOKEN都是一些用戶名+時間等內容的MD5的不可逆加密.然后通過一個USER_TOKEN表來判斷用戶請求中包含的TOKEN與USER_TOKEN表中的TOKEN是否一致即可.
具體實踐過程主要為:
設定一個密鑰比如key = ‘2323dsfadfewrasa3434"。
這個key 只有發送方和接收方知道。
調用時,發送方,組合各個參數用密鑰 key按照一定的規則(各種排序,MD5,ip等)生成一個access_key。一起post提交到API接口。
接收方拿到post過來的參數以及這個access_key。也和發送一樣,用密鑰key 對各個參數進行一樣的規則(各種排序,MD5,ip等)也生成一個access_key2。
對比 access_key 和 access_key2 。一樣。則允許操作,不一樣,報錯返回或者加入黑名單。
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
express: 我們的主要開發框架
mongoose: 用來與MongoDB數據庫進行交互的框架,請提前安裝好MongoDB在PC上
morgan: 會將程序請求過程的信息顯示在Terminal中,以便于我們調試代碼
jsonwebtoken: 用來生成我們的token
passport: 非常流行的權限驗證庫
bcrypt: 對用戶密碼進行hash加密
-- 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配置
./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開發上有所啟發,小生才疏學淺,過程中有什么不足的地方也歡迎指正。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/18957.html
摘要:則是目前比較成熟的一套互聯網應用程序的設計理論。則允許操作,不一樣,報錯返回或者加入黑名單。再看下我們的數據庫中的用戶信息,值也被存入了進來,便于我們之后進行權限驗證。訪問同時將我們的值在中以傳入正確獲得用戶名則表示我們訪問請求通過了驗證。 showImg(https://segmentfault.com/img/remote/1460000008629635?w=800&h=534)...
摘要:異步最佳實踐避免回調地獄前端掘金本文涵蓋了處理異步操作的一些工具和技術和異步函數。 Nodejs 連接各種數據庫集合例子 - 后端 - 掘金Cassandra Module: cassandra-driver Installation ... 編寫 Node.js Rest API 的 10 個最佳實踐 - 前端 - 掘金全文共 6953 字,讀完需 8 分鐘,速讀需 2 分鐘。翻譯自...
摘要:簡評之前,后端開發路線圖僅僅是一個技術推薦,且沒有明確的方向指明應該遵循的順序,這份重新制作的指南將會給你一個更好的方向。現在開始創建一個包并分發給其他人使用,并確保遵循迄今為止學到的標準和最佳實踐。 簡評:之前,后端開發路線圖僅僅是一個技術推薦,且沒有明確的方向指明應該遵循的順序,這份重新制作的指南將會給你一個更好的方向。 現在的 Web 開發與幾年前完全不同了,有很多不同的東西可以...
摘要:七牛云接入本系統的圖片,音視頻是放在七牛云,所以需要接入七牛云。在服務端通過接口請求來獲取七牛云上傳,客戶端獲取到七牛云,通過不同方案將帶上。 效果展示 showImg(https://user-gold-cdn.xitu.io/2018/8/26/16576a709bd02f5f?w=1409&h=521&f=gif&s=30128195); showImg(https://user...
閱讀 2706·2021-11-11 16:54
閱讀 2329·2021-10-09 09:44
閱讀 2548·2019-08-30 15:54
閱讀 1936·2019-08-30 11:24
閱讀 1175·2019-08-29 17:03
閱讀 2107·2019-08-29 16:22
閱讀 2086·2019-08-29 13:11
閱讀 1044·2019-08-29 12:14