egg-passport 使用 和源码 分析

2020-05-03  本文已影响0人  4a869234fdc5

写教程的环境

  "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策略,用来持续化存取用户信息。

代码顺序

  1. 在 plugin.js 添加 egg-passport 插件,并打开。

  2. 在 router.js 添加 需要验证的地址
    router.post('/login', app.passport.authenticate('local', { successRedirect: '/authCallback' }));

  3. 新建 app.js 引入 passport,添加验证规则,这样程序就会加入到 passport 的私有数组, _verifyHooks。存起来

  app.passport.verify(async (ctx, user) => {
    // ctx.logger.error(new Error('whoops'));

    ctx.logger.debug('debug info');

    return user
  });
  1. 实例化我们的策略
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); // 触发我们添加的验证规则
  }));

  1. 可以看出 核心流程 就是 添加verify->触发verifiy 就那么简单。

API

egg-passport 提供了以下扩展:

还提供了 API:

注意:

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;

上一篇 下一篇

猜你喜欢

热点阅读