写教程的环境
"dependencies": {
"egg": "^2.26.0",
"egg-passport": "^2.1.1",
}
egg-passport 其实就是封装的 passport
passport官方文档
passport 代码核心就是简单验证。 而验证规则 则是通过 单独 的 策略。eggjs 通过注入 app 实例,挂载 egg-passport 插件。然后通过 app.passport.use(Strategy) 启用策略,而 use 函数 是通过 super()继承 而来。
ps: 框架 自带 一个 session策略,用来持续化存取用户信息。
代码顺序
-
在 plugin.js 添加 egg-passport 插件,并打开。
-
在 router.js 添加 需要验证的地址
router.post('/login', app.passport.authenticate('local', { successRedirect: '/authCallback' })); -
新建 app.js 引入 passport,添加验证规则,这样程序就会加入到 passport 的私有数组, _verifyHooks。存起来
app.passport.verify(async (ctx, user) => {
// ctx.logger.error(new Error('whoops'));
ctx.logger.debug('debug info');
return user
});
- 实例化我们的策略
const LocalStrategy = require('passport-local').Strategy;
app.passport.use(new LocalStrategy({
passReqToCallback: true, // passReqToCallback 意思是 在下面的 回调函数中是否包含 requet 请求,也就是第一个参数 req。如只验证username,和 password 可以关闭。
}, (req, username, password, done) => {
// 编辑 user 格式
const user = {
provider: 'local', // 策略的名字
username,
password,
};
app.logger.debug('%s %s get user: %j', req.method, req.url, user);
app.passport.doVerify(req, user, done); // 触发我们添加的验证规则
}));
- 可以看出 核心流程 就是 添加verify->触发verifiy 就那么简单。
API
egg-passport 提供了以下扩展:
-
ctx.user
- 获取当前已登录的用户信息 -
ctx.isAuthenticated()
- 检查该请求是否已授权 -
ctx.login(user, [options])
- 为用户启动一个登录的 session -
ctx.logout()
- 退出,将用户信息从 session 中清除 -
ctx.session.returnTo=
- 在跳转验证前设置,可以指定成功后的 redirect 地址
还提供了 API:
-
app.passport.verify(async (ctx, user) => {})
- 校验用户 -
app.passport.serializeUser(async (ctx, user) => {})
- 序列化用户信息后存储进 session -
app.passport.deserializeUser(async (ctx, user) => {})
- 反序列化后取出用户信息 -
app.passport.authenticate(strategy, options)
- 生成指定的鉴权中间件-
options.successRedirect
- 指定鉴权成功后的 redirect 地址 -
options.loginURL
- 跳转登录地址,默认为/passport/${strategy}
-
options.callbackURL
- 授权后回调地址,默认为/passport/${strategy}/callback
-
-
app.passport.mount(strategy, options)
- 语法糖,方便开发者配置路由
注意:
-
app.passport.authenticate
中,未设置options.successRedirect
或者options.successReturnToOrRedirect
将默认跳转/
egg-passport 序列化和反序列化
egg-passport 重写了 passport 的 序列化方法 流程和 verify 差不多,。通过 cookie EGG_SESS 存着用户的信息。每次请求会验证用户 seesion id 对应用户信息。
通过添加 serializeUser()添加 序列化函数,然后 框架会自动 在验证完用户的时候 存session 的时候执行。
通过deserializeUser()添加反序列化函数,在通过 session 获取用户的时候。
serializeUser 和 deserializeUser 必须有。否则无法保存用户信息。
// 必须返回 user 哦。 两个都要返回 。不然 获取不到用户
app.passport.serializeUser(async (ctx, user) => {
return user
});
app.passport.deserializeUser(async (ctx, user) => {
return user
});
//源码:
'use strict';
constdebug=require('debug')('egg-passport:passport');
constPassport=require('passport').Passport;
constSessionStrategy=require('passport').strategies.SessionStrategy;
constframework=require('./framework');
consturl=require('url');
classEggPassportextendsPassport{
constructor(app){
super();
this.app=app;
this._verifyHooks=[];
this._serializeUserHooks=[];
this._deserializeUserHooks=[];
}
/**
* Overide the initialize authenticator to make sure `__monkeypatchNode` run once.
*/
init(){
this.framework(framework);
this.use(newSessionStrategy());
}
/**
* Middleware that will authorize a third-party account using the given
* `strategy` name, with optional `options`.
*
* Examples:
*
* passport.authorize('twitter', { failureRedirect: '/account' });
*
*@param{String} strategy - strategy provider name
*@param{Object} [options] - optional params
*@return{Function} middleware
*@apipublic
*/
authenticate(strategy,options={}){
// try to use successReturnToOrRedirect first
if(!options.hasOwnProperty('successRedirect')&&!options.hasOwnProperty('successReturnToOrRedirect')){
// app use set `ctx.session.returnTo = ctx.path` before auth redirect
options.successReturnToOrRedirect='/';
}
if(!options.hasOwnProperty('failWithError')){
options.failWithError=true;
}
returnsuper.authenticate(strategy,options);
}
session(){
returnthis._framework.session();
}
mount(strategy,options={}){
options.loginURL=options.loginURL||`/passport/${strategy}`;
options.callbackURL=options.callbackURL||`/passport/${strategy}/callback`;
constauth=this.authenticate(strategy,options);
this.app.get(url.parse(options.loginURL).pathname,auth);
this.app.get(url.parse(options.callbackURL).pathname,auth);
}
doVerify(req,user,done){
consthooks=this._verifyHooks;
if(hooks.length===0)returndone(null,user);
(async()=>{
constctx=req.ctx;
for(consthandlerofhooks){
user=awaithandler(ctx,user);
if(!user){
break;
}
}
done(null,user);
})().catch(done);
}
/**
* Verify authenticated user
*
*@param{Function} handler - verify handler
*/
verify(handler){
this._verifyHooks.push(this.app.toAsyncFunction(handler));
}
serializeUser(handler){
if(typeofhandler==='function'){
// serializeUser(async function (ctx, user))
this._serializeUserHooks.push(this.app.toAsyncFunction(handler));
}elseif(arguments.length===3){
// passport => http/request.js call passport.serializeUser(verifiedUser, req, done)
constverifiedUser=arguments[0];
constreq=arguments[1];
constdone=arguments[2];
returnthis._handleSerializeUser(req.ctx,verifiedUser,done);
}else{
debug(arguments);
thrownewError('Unkown serializeUser called');
}
}
deserializeUser(handler){
if(typeofhandler==='function'){
// deserializeUser(async function (ctx, user))
this._deserializeUserHooks.push(this.app.toAsyncFunction(handler));
}else{
// return promise
constctx=arguments[0];
constsessionUser=arguments[1];
returnthis._handleDeserializeUser(ctx,sessionUser);
}
}
_handleSerializeUser(ctx,verifiedUser,done){
consthooks=this._serializeUserHooks;
debug('serializeUserHooks length: %d',hooks.length);
// make sure profile proerty cleanup
if(verifiedUser&&verifiedUser.profile){
verifiedUser.profile=undefined;
}
if(hooks.length===0)returndone(null,verifiedUser);
(async()=>{
letsessionUser=verifiedUser;
for(consthandlerofhooks){
sessionUser=awaithandler(ctx,sessionUser);
if(!sessionUser){
break;
}
}
debug('serializeUser %j => %j',verifiedUser,sessionUser);
done(null,sessionUser);
})().catch(done);
}
async_handleDeserializeUser(ctx,sessionUser){
consthooks=this._deserializeUserHooks;
debug('deserializeUserHooks length: %d',hooks.length);
if(hooks.length===0)returnsessionUser;
letuser=sessionUser;
for(consthandlerofhooks){
user=awaithandler(ctx,user);
if(!user){
break;
}
}
debug('deserializeUser %j => %j',sessionUser,user);
returnuser;
}
}
module.exports=EggPassport;
网友评论