美文网首页
egg-passport 使用 和源码 分析

egg-passport 使用 和源码 分析

作者: 4a869234fdc5 | 来源:发表于2020-05-03 00:05 被阅读0次

    写教程的环境

      "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 提供了以下扩展:

    • 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;
    
    

    相关文章

      网友评论

          本文标题:egg-passport 使用 和源码 分析

          本文链接:https://www.haomeiwen.com/subject/kmflghtx.html