摘要:則是目前比較成熟的一套互聯(lián)網(wǎng)應(yīng)用程序的設(shè)計(jì)理論。則允許操作,不一樣,報(bào)錯(cuò)返回或者加入黑名單。再看下我們的數(shù)據(jù)庫中的用戶信息,值也被存入了進(jìn)來,便于我們之后進(jìn)行權(quán)限驗(yàn)證。訪問同時(shí)將我們的值在中以傳入正確獲得用戶名則表示我們?cè)L問請(qǐng)求通過了驗(yàn)證。
關(guān)于安全性方面的建議
可以參考這篇總結(jié) 開發(fā)安全的 API 所需要核對(duì)的清單
安裝git clone https://github.com/Nicksapp/nAuth-restful-api.git
運(yùn)行npm install
具體數(shù)據(jù)庫配置信息在config.js中設(shè)置
整體構(gòu)架開發(fā)前先進(jìn)行我們?cè)O(shè)計(jì)的構(gòu)想
路由設(shè)計(jì)
POST /api/signup: 用戶注冊(cè)
POST /api/user/accesstoken: 賬號(hào)驗(yàn)證,獲取token
GET /api/users/info: 獲得用戶信息,需驗(yàn)證
user 模型設(shè)計(jì)
name : 用戶名
password: 密碼
token: 驗(yàn)證相關(guān)token
關(guān)于RESTful API網(wǎng)上已經(jīng)有了很多關(guān)于RESTful的介紹,我這里也不過多重復(fù)了。想說的就是它的主要作用,就是對(duì)于現(xiàn)如今的網(wǎng)絡(luò)應(yīng)用程序,分為前端和后端兩個(gè)部分,然而當(dāng)前的發(fā)展趨勢就是應(yīng)用平臺(tái)需求的擴(kuò)大(IOS、Android、Webapp等等)
因此,就需要一種統(tǒng)一的機(jī)制,方便不同的應(yīng)用平臺(tái)的前端設(shè)備與后端進(jìn)行通信,也就是前后端的分離。這導(dǎo)致了API架構(gòu)的流行,甚至出現(xiàn)"API First"的設(shè)計(jì)思想。RESTful API則是目前比較成熟的一套互聯(lián)網(wǎng)應(yīng)用程序的API設(shè)計(jì)理論。
技術(shù)棧使用Node.js上的Express框架進(jìn)行我們的路由設(shè)計(jì),Mongoose來與Mongodb數(shù)據(jù)庫連接交互,使用Postman對(duì)我們?cè)O(shè)計(jì)的Api進(jìn)行調(diào)試,快動(dòng)起手來吧!
API設(shè)計(jì)中的token的思路在API設(shè)計(jì)中,TOKEN用來判斷用戶是否有權(quán)限訪問API.TOKEN首先不需要編解碼處理. 一般TOKEN都是一些用戶名+時(shí)間等內(nèi)容的MD5的不可逆加密.然后通過一個(gè)USER_TOKEN表來判斷用戶請(qǐng)求中包含的TOKEN與USER_TOKEN表中的TOKEN是否一致即可.
具體實(shí)踐過程主要為:
設(shè)定一個(gè)密鑰比如key = ‘2323dsfadfewrasa3434"。
這個(gè)key 只有發(fā)送方和接收方知道。
調(diào)用時(shí),發(fā)送方,組合各個(gè)參數(shù)用密鑰 key按照一定的規(guī)則(各種排序,MD5,ip等)生成一個(gè)access_key。一起post提交到API接口。
接收方拿到post過來的參數(shù)以及這個(gè)access_key。也和發(fā)送一樣,用密鑰key 對(duì)各個(gè)參數(shù)進(jìn)行一樣的規(guī)則(各種排序,MD5,ip等)也生成一個(gè)access_key2。
對(duì)比 access_key 和 access_key2 。一樣。則允許操作,不一樣,報(bào)錯(cuò)返回或者加入黑名單。
token設(shè)計(jì)具體實(shí)踐廢話不多說,先進(jìn)入看我們的干貨,這次選用Node.js+experss配合Mongoose來進(jìn)入REST的token實(shí)踐
項(xiàng)目地址: GitHub地址
或 git clone https://github.com/Nicksapp/nAuth-restful-api.git
新建項(xiàng)目先看看我們的項(xiàng)目文件夾
- routes/ ---- index.js ---- users.js - models/ ---- user.js - config.js - package.json - passport.js - index.js
npm init創(chuàng)建我們的package.json
接著在項(xiàng)目根文件夾下安裝我們所需的依賴
npm install express body-parser morgan mongoose jsonwebtoken bcrypt passport passport-http-bearer --save
express: 我們的主要開發(fā)框架
mongoose: 用來與MongoDB數(shù)據(jù)庫進(jìn)行交互的框架,請(qǐng)?zhí)崆鞍惭b好MongoDB在PC上
morgan: 會(huì)將程序請(qǐng)求過程的信息顯示在Terminal中,以便于我們調(diào)試代碼
jsonwebtoken: 用來生成我們的token
passport: 非常流行的權(quán)限驗(yàn)證庫
bcrypt: 對(duì)用戶密碼進(jìn)行hash加密
-- save會(huì)將我們安裝的庫文件寫入package.json的依賴中,以便其他人打開項(xiàng)目是能夠正確安裝所需依賴.
用戶模型定義我們所需用戶模型,用于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, // 不可重復(fù)約束 require: true // 不可為空約束 }, password: { type: String, require: true }, token: { type: String } }); // 添加用戶保存時(shí)中間件對(duì)password進(jìn)行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(); } }); // 校驗(yàn)用戶輸入密碼是否正確 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數(shù)據(jù)庫連接和token的密鑰。
module.exports = { "secret": "learnRestApiwithNickjs", // used when we create and verify JSON Web Tokens "database": "mongodb://localhost:27017/test" // 填寫本地自己 mongodb 連接地址,xxx為數(shù)據(jù)表名 };本地服務(wù)器配置
./index.js 服務(wù)器配置文件,也是程序的入口。
這里我們主要用來包含我們程序需要加載的庫文件,調(diào)用初始化程序所需要的依賴。
const express = require("express"); const app = express(); const bodyParser = require("body-parser");// 解析body字段模塊 const morgan = require("morgan"); // 命令行l(wèi)og顯示 const mongoose = require("mongoose"); const passport = require("passport");// 用戶認(rèn)證模塊passport const Strategy = require("passport-http-bearer").Strategy;// token驗(yàn)證模塊 const routes = require("./routes"); const config = require("./config"); let port = process.env.PORT || 8080; app.use(passport.initialize());// 初始化passport模塊 app.use(morgan("dev"));// 命令行中顯示程序運(yùn)行日志,便于bug調(diào)試 app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); // 調(diào)用bodyParser模塊以便程序正確解析body傳入值 routes(app); // 路由引入 mongoose.Promise = global.Promise; mongoose.connect(config.database); // 連接數(shù)據(jù)庫 app.listen(port, () => { console.log("listening on port : " + port); })路由配置
./routes 主要存放路由相關(guān)文件
./routes/index.js 路由總?cè)肟冢胨褂寐酚?/p>
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); // 注冊(cè)賬戶 router.post("/signup", (req, res) => { if (!req.body.name || !req.body.password) { res.json({success: false, message: "請(qǐng)輸入您的賬號(hào)密碼."}); } else { var newUser = new User({ // 在庫中創(chuàng)建一個(gè)新用戶 name: req.body.name, password: req.body.password }); // 保存用戶賬號(hào) newUser.save((err) => { if (err) { return res.json({success: false, message: "注冊(cè)失敗!"}); } res.json({success: true, message: "成功創(chuàng)建新用戶!"}); }); } }); // 檢查用戶名與密碼并生成一個(gè)accesstoken如果驗(yàn)證通過 router.post("/user/accesstoken", (req, res) => { User.findOne({ // 根據(jù)用戶名查找是否存在該用戶 name: req.body.name }, (err, user) => { if (err) { throw err; } if (!user) { res.json({success: false, message:"認(rèn)證失敗,用戶不存在!"}); } 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 過期銷毀時(shí)間設(shè)置 }); user.token = token; user.save(function(err){ if (err) { res.send(err); } }); res.json({ success: true, message: "驗(yàn)證成功!", token: "Bearer " + token, name: user.name }); } else { res.send({success: false, message: "認(rèn)證失敗,密碼錯(cuò)誤!"}); } }); } }); }); // passport-http-bearer token 中間件驗(yàn)證 // 通過 header 發(fā)送 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 配置權(quán)限模塊所需功能
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); }); } )); };
主要驗(yàn)證發(fā)送的token值與用戶服務(wù)器端token值是否匹配,進(jìn)行信息驗(yàn)證。
具體調(diào)試現(xiàn)在就可以運(yùn)行我們的代碼看具體運(yùn)作過程了!為了便于調(diào)試與參數(shù)的收發(fā),我們使用postman(可在Chrome上或Mac上安裝)來操作.
node index運(yùn)行我們的本地服務(wù)器,訪問 [localhost:8080/]()
應(yīng)該就可以看到我們所返回的初始json值了,然我們繼續(xù)深入測試。
POST訪問[localhost:8080/api/signup](),我們來注冊(cè)一個(gè)新用戶,注意要設(shè)置body的Content-Type為x-www-form-urlencoded 以便我們的body-parser能夠正確解析,好的我們成功模擬創(chuàng)建了我們的新用戶。
連接一下數(shù)據(jù)庫看下我們的用戶信息是否也被正確存儲(chǔ)(注:我使用的是MongoChef,十分強(qiáng)大MongoDB數(shù)據(jù)庫管理軟件),我們可以看到,我的password也被正確加密保存了。
接著POST訪問[localhost:8080/api/user/accesstoken](),來為我的用戶獲得專屬token,POST過程與注冊(cè)相關(guān),可以看到也正確生成我們的token值。
再看下我們的數(shù)據(jù)庫中的用戶信息,token值也被存入了進(jìn)來,便于我們之后進(jìn)行權(quán)限驗(yàn)證。
GET訪問[localhost:8080/api/user/user_info](),同時(shí)將我們的token值在Header中以 Authorization: token 傳入,正確獲得用戶名則表示我們?cè)L問請(qǐng)求通過了驗(yàn)證。
如果token值不正確,則返回HTTP狀態(tài)碼 401 Unauthorized 并拒絕訪問請(qǐng)求。到這里我們的權(quán)限驗(yàn)證功能也就基本實(shí)現(xiàn)了。
希望在看完這篇教程后能夠?qū)δ阍赗ESTful Api開發(fā)上有所啟發(fā),小生才疏學(xué)淺,過程中有什么不足的地方也歡迎指正。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/86863.html
摘要:則是目前比較成熟的一套互聯(lián)網(wǎng)應(yīng)用程序的設(shè)計(jì)理論。則允許操作,不一樣,報(bào)錯(cuò)返回或者加入黑名單。再看下我們的數(shù)據(jù)庫中的用戶信息,值也被存入了進(jìn)來,便于我們之后進(jìn)行權(quán)限驗(yàn)證。訪問同時(shí)將我們的值在中以傳入正確獲得用戶名則表示我們?cè)L問請(qǐng)求通過了驗(yàn)證。 showImg(https://segmentfault.com/img/remote/1460000008629635?w=800&h=534)...
摘要:異步最佳實(shí)踐避免回調(diào)地獄前端掘金本文涵蓋了處理異步操作的一些工具和技術(shù)和異步函數(shù)。 Nodejs 連接各種數(shù)據(jù)庫集合例子 - 后端 - 掘金Cassandra Module: cassandra-driver Installation ... 編寫 Node.js Rest API 的 10 個(gè)最佳實(shí)踐 - 前端 - 掘金全文共 6953 字,讀完需 8 分鐘,速讀需 2 分鐘。翻譯自...
摘要:簡評(píng)之前,后端開發(fā)路線圖僅僅是一個(gè)技術(shù)推薦,且沒有明確的方向指明應(yīng)該遵循的順序,這份重新制作的指南將會(huì)給你一個(gè)更好的方向。現(xiàn)在開始創(chuàng)建一個(gè)包并分發(fā)給其他人使用,并確保遵循迄今為止學(xué)到的標(biāo)準(zhǔn)和最佳實(shí)踐。 簡評(píng):之前,后端開發(fā)路線圖僅僅是一個(gè)技術(shù)推薦,且沒有明確的方向指明應(yīng)該遵循的順序,這份重新制作的指南將會(huì)給你一個(gè)更好的方向。 現(xiàn)在的 Web 開發(fā)與幾年前完全不同了,有很多不同的東西可以...
摘要:接下來繼續(xù)介紹三種架構(gòu)模式,分別是查詢分離模式微服務(wù)模式多級(jí)緩存模式。分布式應(yīng)用程序可以基于實(shí)現(xiàn)諸如數(shù)據(jù)發(fā)布訂閱負(fù)載均衡命名服務(wù)分布式協(xié)調(diào)通知集群管理選舉分布式鎖和分布式隊(duì)列等功能。 SpringCloud 分布式配置 SpringCloud 分布式配置 史上最簡單的 SpringCloud 教程 | 第九篇: 服務(wù)鏈路追蹤 (Spring Cloud Sleuth) 史上最簡單的 S...
摘要:七牛云接入本系統(tǒng)的圖片,音視頻是放在七牛云,所以需要接入七牛云。在服務(wù)端通過接口請(qǐng)求來獲取七牛云上傳,客戶端獲取到七牛云,通過不同方案將帶上。 效果展示 showImg(https://user-gold-cdn.xitu.io/2018/8/26/16576a709bd02f5f?w=1409&h=521&f=gif&s=30128195); showImg(https://user...
閱讀 2376·2021-09-22 15:15
閱讀 640·2021-09-02 15:11
閱讀 1784·2021-08-30 09:48
閱讀 1884·2019-08-30 15:56
閱讀 1480·2019-08-30 15:52
閱讀 2042·2019-08-30 15:44
閱讀 431·2019-08-29 16:29
閱讀 1538·2019-08-29 11:06