前言
在我之前的【Node服務(wù)集成權(quán)限認(rèn)證中間件之Passport】一文中已經(jīng)介紹了如何為普通express應(yīng)用集成passport認(rèn)證中間件。那么對(duì)于下一代的Node應(yīng)用框架——Koa廉邑,能否完美集成passport中間件呢缠沈?
困難
不得不承認(rèn),對(duì)于Koa而言谬以,尤其是最新版本2.X版本Koa速和,其生態(tài)目前完全出于一個(gè)起步階段枣购,對(duì)比目前express大量穩(wěn)定可用的中間件,的確是有許多的不足免姿。但是開(kāi)源的力量永遠(yuǎn)是最強(qiáng)大的饼酿,只要有需求,就一定有解決方案
思路
1胚膊、首選故俐,passport中間件的官方只是提供了對(duì)express的支持。但是相對(duì)應(yīng)的紊婉,koa也有其對(duì)應(yīng)的開(kāi)源集成版本——【koa-passport】
2药版、這里需要注意的是,koa-passport本質(zhì)上并非全新實(shí)現(xiàn)的另外一個(gè)版本的passport喻犁,它僅僅只是作為中間件槽片,將原先passport對(duì)express的支持,轉(zhuǎn)移到了koa上肢础。通過(guò)分析其源碼可以看出还栓,它其實(shí)是將原先綁定在express的req和res中的API方法,重新綁定到了koa的ctx中
3传轰、美中不足的是koa-passport的官方文檔中并沒(méi)有給出很詳細(xì)的使用方法剩盒,其koa-passport-example項(xiàng)目的示例也沒(méi)有給出很清晰的使用說(shuō)明,就我個(gè)人直接使用koa-passport-example項(xiàng)目慨蛙,如果不加以修改辽聊,是無(wú)法完成認(rèn)證功能的,所以難免會(huì)對(duì)初學(xué)者造成困擾期贫,甚至是放棄
解決
對(duì)于koa-passport集成進(jìn)入koa2版本跟匆,雖然是google和baidu了大量資料,但是很遺憾唯灵,目前似乎并沒(méi)有太多關(guān)于這兩者的集成文章贾铝,相關(guān)的都是比較老版本的文章或者是經(jīng)過(guò)實(shí)踐并沒(méi)有生效的文章,并沒(méi)有太多的參考價(jià)值
所以埠帕,這一篇文章可能是全網(wǎng)第一篇koa2版本正確集成koa-passport的教程垢揩,通過(guò)較長(zhǎng)時(shí)間的分析koa-passport和passport的源碼,最終得出了以下的正確集成方法敛瓷,在此作為文章分享出來(lái)
步驟
第一叁巨,安裝koa-passport
npm install koa-bodyparser --save
npm install passport --save
npm install passport-local --save
npm install koa-passport --save
npm install koa-session2 --save 【官方示例文檔上使用的是koa-session,存在兼容問(wèn)題】
第二呐籽,創(chuàng)建passport配置文件passport_config.js
【UserModel是數(shù)據(jù)庫(kù)查詢封裝锋勺,在此省略】
const passport = require('koa-passport')
const LocalStrategy = require('passport-local')
const UserModel = require(__dirname + '/../../src/model/UserModel')
// 用戶名密碼驗(yàn)證策略
passport.use(new LocalStrategy(
/**
* @param username 用戶輸入的用戶名
* @param password 用戶輸入的密碼
* @param done 驗(yàn)證驗(yàn)證完成后的回調(diào)函數(shù)蚀瘸,由passport調(diào)用
*/
function (username, password, done) {
let where = { where: { username: username } }
UserModel.findOne(where).then(function (result) {
if (result != null) {
if (result.password == password) {
return done(null, result)
} else {
return done(null, false, '密碼錯(cuò)誤')
}
} else {
return done(null, false, '未知用戶')
}
}).catch(function (err) {
log.error(err.message)
return done(null, false, { message: err.message })
})
}
))
// serializeUser 在用戶登錄驗(yàn)證成功以后將會(huì)把用戶的數(shù)據(jù)存儲(chǔ)到 session 中
passport.serializeUser(function (user, done) {
done(null, user)
})
// deserializeUser 在每次請(qǐng)求的時(shí)候?qū)?session 中讀取用戶對(duì)象
passport.deserializeUser(function (user, done) {
return done(null, user)
})
module.exports = passport
第三,創(chuàng)建認(rèn)證路由xauth.js
【這里使用的是koa-router的koa2版本】
// 路由相關(guān)
const Router = require('koa-router')
// 初始化路由
const router = new Router()
// 認(rèn)證相關(guān)
const passport = require(__dirname + '/passport_config.js')
/**
* 認(rèn)證登錄
*/
router.post('/xauth/login', function (ctx, next) {
return passport.authenticate('local', function (err, user, info, status) {
if (user) {
ctx.body = 'Y'
return ctx.login(user)
} else {
ctx.body = info
}
})(ctx, next)
})
/**
* 認(rèn)證登出
*/
router.get('/xauth/logout', function (ctx, next) {
ctx.logout()
ctx.body = 'Y'
})
// 以下為自定義需要身份認(rèn)證的路由
router.post('/xauth/test', function (ctx, next) {
if (ctx.isAuthenticated()) {
ctx.body = '認(rèn)證通過(guò)'
} else {
ctx.throw(401)
ctx.body = '非法訪問(wèn)'
}
})
module.exports = router
最后庶橱,Koa的App中初始化和引入passport
// 認(rèn)證相關(guān)
const bodyParser = require('koa-bodyparser')
const mount = require('koa-mount')
const session = require("koa-session2")
const passport = require(__dirname + '/src/auth/passport_config.js')
const xauth = require(__dirname + '/src/auth/xauth.js')
// 初始化應(yīng)用服務(wù)
const app = new Koa()
// 啟用認(rèn)證路由
app.proxy = true
app.use(session({key: "SESSIONID"}))
app.use(bodyParser())
app.use(passport.initialize())
app.use(passport.session())
app.use(mount('/',xauth.routes()))
后記
關(guān)于本文中提到的內(nèi)容贮勃,引用于我的開(kāi)源項(xiàng)目【XServer——組件化后端構(gòu)建】,本文代碼開(kāi)源于Github的【x-koa——組件化后端開(kāi)發(fā)模板】苏章,十分感謝對(duì)于本文的閱讀寂嘉,如有任何的疑問(wèn)或者建議甚至批評(píng),可以隨時(shí)通過(guò)評(píng)論或私信聯(lián)系我枫绅,我會(huì)第一時(shí)間回復(fù)