上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
返回列表 发新帖

nestjs passport及@nestjs/passport源码级分析(三)

[复制链接]
  • TA的每日心情
    开心
    2023-2-28 23:59
  • 签到天数: 191 天

    [LV.7]常住居民III

    637

    主题

    5194

    回帖

    6076

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6076

    荣誉开发者管理员油中2周年生态建设者喜迎中秋

    发表于 2022-12-4 01:34:19 | 显示全部楼层 | 阅读模式

    前言

    之前我们已经分析了
    路由守卫函数会回调canActive
    然后调用Passport的authenticate函数
    然后成功会调用我们自己的handleRequest函数
    那我们继续往authenticate里找
    注意,这个在passport的npm包里
    https://github.com/jaredhanson/passport

    正文

    根据index.js定位到默认导出是new Passport()
    找到了lib\authenticator.js
    可以找到源码

     * @param {String} strategy
     * @param {Object} options
     * @param {Function} callback
     * @Return {Function} middleware
     * @Api public
     */
    Authenticator.prototype.authenticate = function(strategy, options, callback) {
      return this._framework.authenticate(this, strategy, options, callback);
    };

    也就是说strategy是策略,也就是上节传入的'local',options选项,callback回调,而this指的Passport自身,那问题来了,_framework是什么?

    function Authenticator() {
      this._key = 'passport';
      this._strategies = {};
      this._serializers = [];
      this._deserializers = [];
      this._infoTransformers = [];
      this._framework = null;
    
      this.init();
    }
    /**
     * Initialize authenticator.
     *
     * @api protected
     */
    Authenticator.prototype.init = function() {
      this.framework(require('./framework/connect')());
      this.use(new SessionStrategy({ key: this._key }, this.deserializeUser.bind(this)));
      this._sm = new SessionManager({ key: this._key }, this.serializeUser.bind(this));
    };

    初始化将_framework 设为了null,然后调用了init,init内部调用了framwork,并且request了一个对象,我们先看看framwork函数

     * Examples:
     *
     *     passport.framework(require('hapi-passport')());
     *
     * @param {Object} name
     * @return {Authenticator} for chaining
     * @api public
     */
    Authenticator.prototype.framework = function(fw) {
      this._framework = fw;
      return this;
    };

    没错,我们找到了framework,那直接往require('./framework/connect')()里追就好了

    var initialize = require('../middleware/initialize')
      , authenticate = require('../middleware/authenticate');
    /**
     * Framework support for Connect/Express.
     *
     * This module provides support for using Passport with Express.  It exposes
     * middleware that conform to the `fn(req, res, next)` signature.
     *
     * @return {Object}
     * @api protected
     */
    exports = module.exports = function() {
    
      return {
        initialize: initialize,
        authenticate: authenticate
      };
    };

    于是找到了initialize和v函数,我们之前调用的就是authenticate函数,所以往
    require('../middleware/authenticate')追
    找到了lib\middleware\authenticate.js下的
    module.exports = function authenticate(passport, name, options, callback) 函数
    大概样子是

    module.exports = function authenticate(passport, name, options, callback) {
            return function authenticate(req, res, next) {
            }
    }

    一个闭包,我们观察一下之前传入的代码

          passport.authenticate(type, options, (err, user, info, status) => {
            try {
              request.authInfo = info;
              return resolve(callback(err, user, info, status));
            } catch (err) {
              reject(err);
            }
          })(request, response, (err) => (err ? reject(err) : resolve()))

    可以看到首先调用函数返回了一个函数,然后哦立刻调用返回的函数传入request, response, (err) => (err ? reject(err) : resolve()
    让其得到执行
    我们先理一下passport如何找到passport-loca策略的吧
    name是策略的名字
    首先执行到了

    function authenticate(passport, name, options, callback) {
      if (!Array.isArray(name)) {
        name = [ name ];
        multi = false;
      }
      return function authenticate(req, res, next) {
          (function attempt(i) {
               var layer = name[i];
               if (!layer) { return allFailed(); 
                     var strategy, prototype;
              if (typeof layer.authenticate == 'function') {
                strategy = layer;
              } else {
                prototype = passport._strategy(layer);
                if (!prototype) { return next(new Error('Unknown authentication strategy "' + layer + '"')); }
                strategy = Object.create(prototype);
              }
               }
          })(0)
      }
    }

    首先将名字转为数组,然后取第0项开始执行,后续其实会遍历所有策略,但是不涉及我们主要分析代码,有兴趣自己clone一份看看,
    取了第0项知道看是不是一个函数,如果是直接赋值为策略,如果不是则调用passport._strategy
    我们是一个字符串的local,所以会调用passport._strategy函数

    Authenticator.prototype._strategy = function(name) {
      return this._strategies[name];
    };

    _strategs直接在_strategies里查找的,应该是一个对象,键是字符串,值则是策略
    那肯定有一个注册到_strategies上的函数,全局搜索找到了

     * @param {String|Strategy} name
     * @param {Strategy} strategy
     * @return {Authenticator} for chaining
     * @api public
     */
    Authenticator.prototype.use = function(name, strategy) {
      if (!strategy) {
        strategy = name;
        name = strategy.name;
      }
      if (!name) { throw new Error('Authentication strategies must have a name'); }
    
      this._strategies[name] = strategy;
      return this;
    };

    这里判断是否传入了名字,如果没传入则取strategy对象的名字,并且设置到_strategies,方便在authenticate中使用
    当找到了策略就会设置一个空对象,然后把策略设置为原型

    strategy = Object.create(prototype);
    strategy.success = function(user, info){}
    strategy.fail = function(challenge, status){}
    strategy.redirect = function(url, status){}    
    strategy.pass = function(){}
    strategy.error = function(err)(){}
    strategy.authenticate(req, options);

    然后设置一些函数,最后调用了策略的authenticate函数,并且传入了req和options
    因为我们设置的是local,所以调用的是passport-local内的逻辑了
    所以这篇就算完了~

    总结

    passport中的authenticate函数主要从存储的对象中找到对应的策略,并且进行调用,使用use的时候会将策略注入到passport对象中

    结语

    撒花~

    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。

    发表回复

    本版积分规则

    快速回复 返回顶部 返回列表