作後端系統避免不了要作權限認證,好比本地用戶登陸,第三方登陸。
權限認證的思路也極其簡單,不外乎就是登陸,登出,路由守護三部分。node
那麼有沒有現成的輪子可用呢?答案是確定的,node發展了這麼迅速,各類npm包層出不窮,總有那麼幾款厲害的。
今天要講的權限認證中間件那就是:passportgit
passport目前有不少已經寫好的登陸策略,好比github登陸,微信登陸,Facebook登陸,google等等。github
官網 http://passportjs.org/docs/redis
官網是英文的,英文差的話不建議看了,去找個demo擼起來纔是正確的學習思路。數據庫
經過一陣摸索,本文決定記錄下koa2具體的使用步驟。npm
koa2中使用的是 koa-passport
這個包。
本地驗證用的是 passport-local
這個策略後端
npm install -S koa-passport
先來看代碼,稍後再作解釋。
這裏使用 passport-local
策略(本地權限認證)爲例子。
由於passport使用以前要定義策略及序列化與反序列化操做,因此把 passport
的配置及策略寫到一個文件passport.js
。api
定義策略微信
// passport.js const passport = require('koa-passport') var LocalStrategy = require('passport-local').Strategy // 序列化ctx.login()觸發 passport.serializeUser(function(user, done) { console.log('serializeUser: ', user) done(null, user.id) }) // 反序列化(請求時,session中存在"passport":{"user":"1"}觸發) passport.deserializeUser(async function(id, done) { console.log('deserializeUser: ', id) var user = {id: 1, username: 'admin', password: '123456'} done(null, user) }) // 提交數據(策略) passport.use(new LocalStrategy({ // usernameField: 'email', // passwordField: 'passwd' }, function(username, password, done) { console.log('LocalStrategy', username, password) var user = {id: 1, username: username, password: password} done(null, user, {msg: 'this is a test'}) // done(err, user, info) })) module.exports = passport
記得文件末 module.exports = passport
導出 passport
cookie
入口載入
而後在 koa
入口 app.js
中載入 passport.js
文件
const passport = require('./passport')
並在適當位置(看下邊 app.js
)使用passport
中間件
app.use(passport.initialize()) app.use(passport.session())
passport
中間件須要用到 session ()因此,你的app.js入口文件相似這樣
// app.js const Koa = require('koa') const bodyParser = require('koa-bodyparser') const static = require('koa-static') const session = require('koa-session') const RedisStore = require('koa-redis') const app = new Koa() const passport = require('./libs/passport') const baseConf = require('./config/base') const redisConf = require('./config/redis') // 基礎中間件 app.use(async (ctx, next) => { const start = Date.now() await next() const ms = Date.now() - start console.log(`${ctx.method} ${ctx.status} ${ctx.url} - ${ms} ms`) }) app.keys = ['123456'] app.use(bodyParser()) app.use(session({ cookie: {secure: false, maxAge:86400000}, store: RedisStore(redisConf.session) }, app)) app.use(passport.initialize()) app.use(passport.session()) var router = require('./routes') router.all('404', '*', ctx => { ctx.status = 404 ctx.body = '404' }) app.use(router.routes()) app.use(router.allowedMethods()) var log = require('./libs/log') app.on('error', (err, ctx) => { log.error(`${ctx.method} ${ctx.url}`, 'Error: ') log.error(err) console.log(err) }) app.listen(baseConf.port) console.log('listening on ' + baseConf.port) module.exports = app
編寫路由
編寫路由及守護中間件。
POST /login
router.post('/login', ctx => { // 會調用策略 return passport.authenticate('local', function(err, user, info, status) { ctx.body = {user, err, info, status} return ctx.login({id: 1, username: 'admin', password: '123456'}) })(ctx) })
GET /logout
router.get('/logout', ctx => { ctx.logout() ctx.body = {auth: ctx.isAuthenticated(), user: ctx.state.user} })
好比你/api/*
的路由須要用戶認證才能訪問
router.use('/api/*', (ctx, next) => { if(ctx.isAuthenticated()) { next() } else { ctx.status = 401 ctx.body = { msg: 'auth fail' } } })
到這裏,本地權限認證基本完成了,post請求 /login
而且提交表單username
,和 password
便可登陸一個用戶。
/logout
退出當前登陸。
使用 passport
這個中間件,必須瞭解其運行步驟和知識點。
passport
以策略來擴展驗證,什麼是策略呢?
好比:本地策略,github登陸策略,微信登陸策略
passport
中間件使用前,須要註冊策略,及實習序列化與反序列化操做。
序列化
經過 passport.serializeUser
函數定義序列化操做。
// 序列化 passport.serializeUser(function(user, done) { done(null, user.id) })
在調用 ctx.login()
時會觸發序列化操做。
反序列化
經過 passport.deserializeUser
函數定義反序列化操做。
// 反序列化 passport.deserializeUser(async function(id, done) { console.log('deserializeUser: ', id) var user = {id: 1, username: 'admin', password: '123456'} done(null, user) })
在請求時,session中若是存在 "passport":{"user":"xxx"}時會觸發定義的反序列化操做。
註冊策略
// 策略 passport.use(new LocalStrategy({ // usernameField: 'email', // passwordField: 'passwd' }, function(username, password, done) { var user = {id: 1, username: username, password: password} done(null, user) }))
在使用 passport.authenticate('策略', ...)
的時候,會執行策略
其餘
app.use(passport.initialize())
會在請求週期ctx
對象掛載如下方法與屬性
另外附上github的認證代碼
安裝包
npm install -S passport-github
在passport.js
載入
var GitHubStrategy = require('passport-github').Strategy
在passport.js
增長代碼
passport.use(new GitHubStrategy({ clientID: githubConf.clientId, clientSecret: githubConf.secret, callbackURL: githubConf.callback }, function(accessToken, refreshToken, profile, done) { // console.log(accessToken, refreshToken, profile) return done(null, {accessToken, refreshToken, profile}) } ))
添加兩個路由
// 調用受權頁面 router.get('/auth/github', ctx => { return passport.authenticate('github', {scope: ['user:email']})(ctx) }) // 受權回調獲得code router.get('/auth/github/callback', async ctx => { return passport.authenticate('github', (err, user, info, status) => { ctx.body = {err, user, info, status} return ctx.login(user) })(ctx) })
以上例子只是模擬,並無涉及數據庫的操做,具體的實現還須要本身按照業務需求實現。
passport使用session來維護會話。對於token驗證的來講,並不能用,因此要實現token驗證的話還須要另外編寫策略才行。
更多詳細用法,請自行到官網查看文檔。