koa2 使用passport权限认证中间件

本文介绍如何使用Passport中间件在Node.js中实现权限认证,包括配置策略、序列化与反序列化用户信息、利用session存储登录状态等关键步骤。

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

今天要讲的权限认证中间件是:passport

passport 是 Node 的认证中间件,它的存在只有一个单一的目的,就是认证请求。

配置 passport

在使用 passport 认证之前需要配置三个部分

  1. 注册策略
  2. 序列化
  3. 反序列化
注册策略
// 安装依赖
npm install -S koa-passport
npm install -S passport-local

在项目最常用的方式是通过登录的用户名和密码进行用户身份认证,passport 也提供这种认证的策略,即 passport-local(本地权限认证)

在使用 passport.authenticate('local', ...) 的时候,会执行策略

// passport.js
const passport = require('koa-passport')
const LocalStrategy = require('passport-local').Strategy

// 提交数据(策略)
passport.use(new LocalString(async function (username, password, done) {
    const user = await User.findOne({ username })
    if (user === null) {
      return done(null, false, '用户不存在')
    }
    if (user.password === password) {
      return done(null, user, '登录成功')
    } else {
      return done(null, false, '密码错误')
    }
  })
)

passport 默认使用 usernamepassword 来验证,但实际上也有很多需要用邮箱来验证的,那么如何实现呢?passport 在策略配置里提供了options 参数,用来设置你要验证的字段名称,即usernameField,使用方法如下

passport.use(new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password'
  },
  function(username, password, done) {
    // ...
  }
));
序列化

通过 passport.serializeUser 函数定义序列化操作

passport.serializeUser(function (user, done) {
  done(null, user)
})

在调用 ctx.login() 时会触发序列化操作,把用户对象存到 session 里。

反序列化

通过 passport.deserializeUser 函数定义反序列化操作

passport.deserializeUser(function (user, done) {
  done(null, user)
})

在请求时,session 中如果存在 "passport":{"user":"xxx"} 时会触发定义的反序列化操作

序列化与反序列化的目的:passport 将维持持久的登录会话。为了使持久会话正常工作,必须将经过身份验证的用户序列化到该会话,并在发出后续请求时将其反序列化。在典型的应用程序中,这很简单,只需序列化用户user,然后在反序列化时按user查找用户。

session

passport 中间件需要用到 session 来保存用户登录信息,可以使用 redis 来存储 session 所以你的入口文件应该类似这样

const Koa = require('koa')
const session require('koa-session')
const Redis require('koa-redis')
const passport require('./passport') // 用户定义的策略文件

const app = new Koa()

// session 的加密处理
app.keys = ['adcd', 'keyskeys']
app.use(session({
  key: 'mt',
  prefix: 'mt:uid',
  store: new Redis()
}, app))

app.use(passport.initialize()) // 初始化 passport
app.use(passport.session()) // 存储用户登录的 session 信息 持久登录会话
登录

项目中关于 passport 的配置已经写好了接下来就是用户登录了,通过 passport 暴露给 ctx 的login()方法建立登录会话

// 登录
router.post('/signin', (ctx, next) => {
  return Passport.authenticate('local', function (err, user, info) {
    if (err) {
      ctx.body = {
        code: -1,
        msg: err
      }
    } else if (user) {
      ctx.body = {
        code: 0,
        user,
        msg: info
      }
      return ctx.login(user)
    } else {
      ctx.body = {
        code: 1,
        msg: info
      }
    }
  })(ctx, next)
})
大致流程

1、post请求 /signin ,执行 passport.js 中定义的策略
2、获取用户提交表单 usernamepassword 的值进行身份认证根据不同的状态返回 return done
3、执行 Passport.authenticate 的回调函数,如果身份认证成功执行 ctx.login(user)
4、触发序列化,将用户信息存储到 session

登录成功后 redis 中的信息

在这里插入图片描述

注意上面的代码里有个 ctx.login(),它不是 http模块原生的方法,也不是koa中的方法,而是passport加上的,passport扩展了http request添加了四种方法。

login(user, options, callback):作用是为登录用户初始化 session。options可设置 session为 false,即不初始化 session,默认为true。
logout():不带参数。作用是登出用户,删除该用户 session。
isAuthenticated():不带参数。作用是测试该用户是否存在于 session 中(即是否已登录)。若存在返回true。事实上这个比登录验证要有用的多,毕竟 session 通常会保留一段时间,在此期间判断用户是否已登录用这个方法就行了。
isUnauthenticated():不带参数。和上面的作用相反。

登出

login() 相反,logout() 方法用来结束登录会话。 调用 logout() 方法会删除 ctx.user 属性并清除登录会话

// 将user从session删除
router.get('/exit', async (ctx, next) => {
  await ctx.logout()
  if (!ctx.isAuthenticated()) {
    ctx.body = {
      code: 0
    }
  } else {
    ctx.body = {
      code: -1
    }
  }
})

登出成功后 redis 中的信息

在这里插入图片描述

路由守护中间件

比如获取用户信息的路由需要用户认证才能访问

router.get('/getUser', ctx => {
  if (ctx.isAuthenticated()) {
    const { username} = ctx.session.passport.user
    ctx.body = {
      user: username
    }
  } else {
    ctx.body = {
      user: ''
    }
  }
})

上面我们只是讲到了local验证,实际上passport支持的验证方法不止这几种,具体支持的验证方案可以参见passport 官网

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值