Contents
  1. 1. 前言
  2. 2. Demo
  3. 3. 验证流程及源码分析
  4. 4. 写在最后

前言

本文是笔者所总结的有关 Nodejs Passport 系列之一;本文将从源码分析的角度,来深入剖析 passport 的认证流程;备注,此部分流程依赖于 Nodejs Passport 系列之二:Passport 源码剖析之初始化流程作为前置条件;

本文为作者原创作品,转载请注明出处;

Demo

此部分将继续使用 Nodejs Passport 系列之二:Passport 源码剖析之初始化流程中的 LocalStrategy 的 demo 来深入分析如何使用 passport 来对请求进行用户身份认证的流程;

验证流程及源码分析

Nodejs Passport 系列之二:Passport 源码剖析之初始化流程中,分别完成了对 Session Strategy 和 Local Strategy 的初始化操作,本篇文章不打算对 Session Strategy 的验证流程进行分析,而是直接通过 Local Strategy 来深入分析 passport 的认证流程;

当 /login 请求到达,会触发 authenticate.js 模块所 export 出来的与 Local Strategy 相关的 authenticate(req, res, next) 方法句柄,该部分的分析参考 Nodejs Passport 系列之二:Passport 源码剖析之初始化流程的章节“注册 Local Strategy 验证流程”的最后一小节的内容;

相关配置代码如下,
1
2
3
4
5
6
7
8
9
10
passport.use(new LocalStrategy(
(username, password, done) => {
User.findByUsername(username, (error, user) => {
if (error) return done(error);
if (!user) return done(null, false);
if (user.password !== password) return done(null, false);
return done(null, user);
});
}
));
1
app.post('/login', passport.authenticate('local', { successReturnToOrRedirect: '/', failureRedirect: '/login' }))

其核心流程图如下,
passport authentication sequence.png

首先,注意以下几点,

  • 在调用 authenticate.js 模块的 authenticate(req, res, next) 方法的时候的闭包,

    • names

      1
      {'local'}

      在配置的时候,可以定义 names 数组,这样针对同一个请求就可以支持多种验证方案既 Strategies;

    • options
      1
      { successReturnToOrRedirect: '/', failureRedirect: '/login' }
  • authenticate.js 模块中的 strategy 实例既是 Local Strategy 实例;不过要注意它的初始化过程,见流程图中的步骤 1.1.2 和 1.1.3;

然后,我们来看一些核心流程,核心流程均用红色的箭头标出,

  1. 1.1.3 $\to$ 1.1.8 这里定义了一系列与验证相关的模板方法,注意三个方法,strategy.success、strategy.fail 和 strategy.success;
  2. 注意 loop 循环,在源码中是通过一个立即执行函数传输一个递增变量 i 实现的,该循环会依次遍历 names,也就是 Strategy 的注册名,然后加载对应的 Strategy 对象进行验证,因此,

    1
    2
    3
    4
    5
    app.post('/token',
    passport.authenticate(['basic', 'oauth2-client-password'], { session: false }),
    oauth2.token(),
    oauth2.errorHandler()
    );

    当遇到上述这样的初始化方式的时候便不用再奇怪了,就相当于对 /token 请求分别依次执行 Basic Strategy 和 OAuth2 Client Password 的验证;

  3. 1.1.9 正是开始执行 Local Strategy 实例的验证流程,通过调用该实例的 authenticate(req, options) 方法,要注意这里用户自定义的回调方法是如何被调用的逻辑,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    passport.use(new LocalStrategy(
    (username, password, done) => {
    User.findByUsername(username, (error, user) => {
    if (error) return done(error);
    if (!user) return done(null, false);
    if (user.password !== password) return done(null, false);
    return done(null, user);
    });
    }
    ));

    通过初始化 LocalStrategy 实例注入了用户自定义的回调函数 $\alpha$,一个匿名回调函数;在上一篇博文中,我们看到,实例化 LocalStrategy 的时候会将用户自定义的函数 $\alpha$ 赋值给实例属性 this._verify;下面来看看其调用过程,1.1.9.1.4,开始回调用户的验证方法 $\alpha$,与 Local Strategy 的相关源码如下,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function verified(err, user, info) {
    if (err) { return self.error(err); }
    if (!user) { return self.fail(info); }
    self.success(user, info);
    }
    try {
    if (self._passReqToCallback) {
    this._verify(req, username, password, verified);
    } else {
    this._verify(username, password, verified);
    }
    } catch (ex) {
    return self.error(ex);
    }

    可以看到回调过程分为两种情况,如果 self._passReqToCallback 为真,则会将 request 做为参数回调给 $\alpha$;开始回调,执行 this._verify 方法也就是 $\alpha$,所以 $\alpha$ 中的参数 done 就是 verfied 方法;

    1
    2
    3
    4
    5
    6
    7
    8
    (username, password, done) => {
    User.findByUsername(username, (error, user) => {
    if (error) return done(error);
    if (!user) return done(null, false);
    if (user.password !== password) return done(null, false);
    return done(null, user);
    });
    }

    然后分别对应了认证异常、失败和成功的处理逻辑,然后分别会回调 strategy.error、strategy.fail 和 strategy.success 等模板方法进行处理;这里要特别注意的是认证成功以后的回调方法,也就是 strategy.success 的方法的执行逻辑,会回调 request.js 模块中的 logIn 方法在 req 对象中添加属性 user,具体过程参考流程图 1.1.9.1.4.1.1.3.1.3;

  4. 若验证通过,则执行 next(),调用下一个 handler 的业务逻辑;

写在最后

Node.js 因为其语言及其灵活,所以,虽然它的编程风格更像是面向过程的开发方式,但是其逻辑非常的简单明了,一气呵成,以至于它能够在 authenticate.js 模块中的 authenticate 方法中不仅定义了与验证流程相关的所有模板方法而且抽象了验证的流程;

Contents
  1. 1. 前言
  2. 2. Demo
  3. 3. 验证流程及源码分析
  4. 4. 写在最后