做后端系统避免不了要做权限认证,比如本地用户登录,第三方登录。
权限认证的思路也比较简单,不外乎就是登录,登出,路由守护三部分。
今天要讲的权限认证中间件是:passport
passport 是 Node 的认证中间件,它的存在只有一个单一的目的,就是认证请求。
配置 passport
在使用 passport 认证之前需要配置三个部分
- 注册策略
- 序列化
- 反序列化
注册策略
// 安装依赖
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 默认使用 username 和 password 来验证,但实际上也有很多需要用邮箱来验证的,那么如何实现呢?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、获取用户提交表单 username 和 password 的值进行身份认证根据不同的状态返回 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 官网
本文介绍如何使用Passport中间件在Node.js中实现权限认证,包括配置策略、序列化与反序列化用户信息、利用session存储登录状态等关键步骤。
1630

被折叠的 条评论
为什么被折叠?



