猿记录

一个记录、分享的博客

您的位置:主页 > 技术专栏 > node >

koa2 使用passport权限认证中间件

2018-08-14 16:21:47 作者:yxl 次阅读 node

做后端系统避免不了要做权限认证,比如本地用户登录,第三方登录。
权限认证的思路也极其简单,不外乎就是登录,登出,路由守护三部分。

那么有没有现成的轮子可用呢?答案是肯定的,node发展了这么迅速,各种npm包层出不穷,总有那么几款厉害的。
今天要讲的权限认证中间件那就是:passport

passport目前有很多已经写好的登录策略,比如github登录,微信登录,Facebook登录,google等等。

官网 http://passportjs.org/docs/

官网是英文的,英文差的话不建议看了,去找个demo撸起来才是正确的学习思路。

通过一阵摸索,本文决定记录下koa2具体的使用步骤。

安装包

koa2中使用的是 koa-passport 这个包。
本地验证用的是 passport-local这个策略

npm install -S koa-passport

代码

先来看代码,稍后再做解释。
这里使用 passport-local 策略(本地权限认证)为例子。
因为passport使用之前要定义策略及序列化与反序列化操作,所以把 passport 的配置及策略写到一个文件passport.js

定义策略

// passport.jsconst 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

入口载入

然后在 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.jsconst 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)
})// 授权回调得到coderouter.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)
})

以上例子只是模拟,并没有涉及数据库的操作,具体的实现还需要自己按照业务需求实现。

如果你想要在只有某些接口不用验证,其他都要走验证可以如下写

// 允许不用验证的接口
var allowUrl = [
    '/admin/user/login',
    '/admin/user/register',
    '/admin/user/logout',
    '/admin/user/dologin',
    '/admin/user/doregister'
];
function getAllowUrl(url) {
    return allowUrl.indexOf(url);
}
router.use('/*', (ctx, next) => {
    var requrl = ctx.request.url;
    if (getAllowUrl(requrl) >= 0) {
        next();
    } else {
        if (ctx.isAuthenticated()) {
            next()
        } else {
            ctx.redirect('/admin/user/login')
        }
    }
})


凡本站注明“本站”或“投稿”的所有文章,版权均属于本站或投稿人,未经本站授权不得转载、摘编或利用其它方式使用上述作品。

编辑:yxl 关键词: koa2
0

网友评论