koa2 使用passport權限認證中間件

作後端系統避免不了要作權限認證,好比本地用戶登陸,第三方登陸。
權限認證的思路也極其簡單,不外乎就是登陸,登出,路由守護三部分。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.jsapi

定義策略微信

// 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 導出 passportcookie

入口載入

而後在 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對象掛載如下方法與屬性

  • ctx.state.user 認證用戶
  • ctx.login(user) 登陸用戶(序列化用戶)
  • ctx.isAuthenticated() 判斷是否定證

github

另外附上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驗證的話還須要另外編寫策略才行。

更多詳細用法,請自行到官網查看文檔。

相關文章
相關標籤/搜索