Интерпретация принципа сеанса из исходного кода сеанса koa

JavaScript

Оригинальная статья:GitHub.com/in u innuendo/no…

предисловие

Сеанс, также известный как «управление сеансом», хранит свойства и информацию о конфигурации, необходимые для определенного сеанса пользователя. Хранится на сервере и сохраняется на протяжении всего сеанса пользователя.

Однако:

  • Что такое сеанс?
  • Сеанс хранится в памяти сервера или он изначально поддерживается веб-сервером?
  • HTTP-запросы не имеют состояния, почему сервер может каждый раз получать ваш сеанс?
  • Закрыть браузер истечет?

Эта статья будетkoa-session(koa官方维护的session中间件)Исходный код подробно объясняет механизм сеанса. Я надеюсь, что после прочтения у вас будет более четкое представление о природе сеанса и разнице между сеансом и файлом cookie.

Базовые знания

Я полагаю, что все знают некоторые понятия о файлах cookie и сессиях. Наиболее распространенное объяснение состоит в том, что файлы cookie хранятся в браузере, а сеансы — на сервере.

Файлы cookie поддерживаются браузерами, а HTTP-запросы передают файлы cookie на сервер в заголовке запроса. То есть каждый раз, когда браузер посещает страницу, сервер может получить файл cookie этого посетителя.

Но о том, где находится сессия на сервере, и как сервер соответствует сессии посетителя, я на самом деле спрашивал у некоторых бэкенд-студентов, и объяснения были расплывчатыми. Потому что обычно сервисная структура поставляется с этой функцией, и она используется напрямую. Какой принцип за этим стоит, на это не стоит обращать внимание.

Если мы использовали структуру koa, мы знаем, что сам koa не может использовать сеанс, что, по-видимому, указывает на то, что сеанс изначально не поддерживается сервером и должен поддерживаться промежуточным программным обеспечением koa-session.

Итак, что это за механизм реализации?Далее мы введем интерпретацию исходного кода.

Интерпретация исходного кода

коа-сессия:GitHub.com/Смотри аааа/Похоть умереть…

Заинтересованным учащимся рекомендуется загрузить код и сначала ознакомиться с ним.

Код, размещенный в процессе интерпретации, частично упрощен

koa-сессионная структура

Глядя на структуру каталогов koa-session, она очень проста, основная логика сосредоточена в context.js.

├── index.js    // 入口
├── lib
│   ├── context.js
│   ├── session.js
│   └── util.js
└── package.json

Сначала дайте карту мозга основного модуля коа-сессии, вы можете сначала посмотреть на нее:

Повторите процесс

Давайте пошагово рассмотрим его выполнение с момента инициализации koa-session:

Давайте посмотрим, как использовать коа-сессин:

const session = require('koa-session');
const Koa = require('koa');
const app = new Koa();

app.keys = ['some secret hurr'];
const CONFIG = {
  key: 'koa:sess',  // 默认值,自定义cookie中的key
  maxAge: 86400000
};

app.use(session(CONFIG, app));  // 初始化koa-session中间件

app.use(ctx => {
  let n = ctx.session.views || 0;   // 每次都可以取到当前用户的session
  ctx.session.views = ++n;
  ctx.body = n + ' views';
});

app.listen(3000);

инициализация

При инициализации сеанса koa он попросит передать экземпляр приложения.

Фактически именно в момент инициализации объект сеанса монтируется в app.context, а объект сеанса создаетсяlib/context.jsЭто происходит из инстанцирования, поэтому используемый нами ctx.session — это класс, созданный самой koa-сессией.

мы открытыkoa-session/index.js:

module.exports = function(opts, app) {
  opts = formatOpts(opts);  // 格式化配置项,设置一些默认值
  extendContext(app.context, opts); // 划重点,给 app.ctx 定义了 session对象

  return async function session(ctx, next) {
    const sess = ctx[CONTEXT_SESSION];
    if (sess.store) await sess.initFromExternal();
    await next();
    if (opts.autoCommit) {
      await sess.commit();
    }
  };
};

Возвращает функцию промежуточного программного обеспечения koa через внутреннюю инициализацию.

Шаг за шагом, formatOpts используется для обработки некоторых параметров по умолчанию, а основная задача extendContext — сделать перехватчик для ctx следующим образом:

function extendContext(context, opts) {
  Object.defineProperties(context, {
    [CONTEXT_SESSION]: {
      get() {
        if (this[_CONTEXT_SESSION]) return this[_CONTEXT_SESSION];
        this[_CONTEXT_SESSION] = new ContextSession(this, opts);
        return this[_CONTEXT_SESSION];
      },
    },
    session: {
      get() {
        return this[CONTEXT_SESSION].get();
      },
      set(val) {
        this[CONTEXT_SESSION].set(val);
      },
      configurable: true,
    }
  });
}

Когда вы переходите к приведенному выше коду, вы фактически монтируете «частный» объект ContextSession в app.context.ctx[CONTEXT_SESSION], есть несколько методов для его инициализации (например, initFromExternal, initFromCookie). Затем смонтируйте «общедоступный» объект сеанса.

Почему мы говорим о «частном» и «общественном»? Вот подробности. Используется тип Symbol, что делает его недоступным для внешнего мира.ctx[CONTEXT_SESSION]. пройти толькоctx.sessionПредоставляет методы (get/set) внешнему миру.

посмотри сноваindex.jsЭкспортированные промежуточные функции

return async function session(ctx, next) {
  const sess = ctx[CONTEXT_SESSION];
  if (sess.store) await sess.initFromExternal();
  await next();
  if (opts.autoCommit) {
    await sess.commit();
  }
};

Здесь будетctx[CONTEXT_SESSION]Экземпляр назначается на sess, а дальше в зависимости от того, есть ли opts.store, происходит вызовsess.initFromExternal, буквально означает, что каждый раз, когда он проходит через промежуточное программное обеспечение, он будет вызывать внешнюю вещь для инициализации сеанса, о чем мы упомянем позже.

Затем посмотрите на выполнение следующего кода, то есть на выполнение нашей бизнес-логики.

await next()

Тогда есть следующее, что, кажется, операция, похожее на сохранение сеанса.

sess.commit();

После анализа кода выше мы видимkoa-sessionОсновной процесс промежуточного программного обеспечения и операции сохранения.

Итак, когда создается сессия? Вернемся к упомянутому выше перехватчикуextendContext, когда он получит HTTP-запрос, онContextSession类Создайте экземпляр объекта сеанса.

То есть сеанс создается и управляется самим промежуточным ПО, а не веб-сервером.

Перейдем к основным функциямContextSession.

Класс ContextSession

Сначала посмотрите на конструктор:

constructor(ctx, opts) {
  this.ctx = ctx;
  this.app = ctx.app;
  this.opts = Object.assign({}, opts);
  this.store = this.opts.ContextStore ? new this.opts.ContextStore(ctx) : this.opts.store;
}

Ни хрена не делать. смотреть внизget()метод:

get() {
  const session = this.session;
  // already retrieved
  if (session) return session;
  
  // unset
  if (session === false) return null;

  // cookie session store
  if (!this.store) this.initFromCookie();
  return this.session;
}

О, оказывается, это одноэлементный паттерн (подождите, пока объект сгенерируется при его использовании, и множественные вызовы будут напрямую использовать первый объект).

Здесь есть суждение, передается ли параметр opts.store, если нет, используйтеinitFromCookie()Чтобы сгенерировать объект сеанса.

Тогда если OPTS.Store пропущено, почему бы ничего не делать, WTF?

Очевидно, нет, помните предложение, упомянутое в инициализацииinitFromExternalвызов функции.

if (sess.store) await sess.initFromExternal();

Итак, здесь нужно выбрать два разных способа генерации сеанса в зависимости от того, есть opts.store или нет.

В: Что такое магазин?

О: Магазин находится по адресуinitFromExternalКак видите, это на самом деле внешнее хранилище.

Q: Какое внешнее хранилище и где он хранится?

A: Студенты, не волнуйтесь, сначала оглянитесь.

initFromCookie
initFromCookie() {
  const ctx = this.ctx;
  const opts = this.opts;

  const cookie = ctx.cookies.get(opts.key, opts);
  if (!cookie) {  
    this.create();
    return;
  }

  let json = opts.decode(cookie); // 打印json的话,会发现居然就是你的session对象!

  if (!this.valid(json)) {  // 判断cookie过期等
    this.create();
    return;
  }

  this.create(json);
}

Здесь мы нашли очень важную информацию, сессия сохраняется непосредственно в куки после шифрования.

насconsole.logодин разjsonпеременная, чтобы проверить:

initFromeExternal
async initFromExternal() {
  const ctx = this.ctx;
  const opts = this.opts;

  let externalKey;
  if (opts.externalKey) {
    externalKey = opts.externalKey.get(ctx);
  } else {
    externalKey = ctx.cookies.get(opts.key, opts);
  }


  if (!externalKey) {
    // create a new `externalKey`
    this.create();
    return;
  }

  const json = await this.store.get(externalKey, opts.maxAge, { rolling: opts.rolling });
  if (!this.valid(json, externalKey)) {
    // create a new `externalKey`
    this.create();
    return;
  }

  // create with original `externalKey`
  this.create(json, externalKey);
}

можно увидетьstore.get(), строка информации хранится в хранилище и может быть получена с помощью get.

И так же постоянно просит позвонитьcreate().

create

create()Что именно он сделал?

create(val, externalKey) {
  if (this.store) this.externalKey = externalKey || this.opts.genid();
  this.session = new Session(this, val);
}

Он судит о магазине, и если есть магазин, он будет установлен наexternalKeyили сгенерировать случайный идентификатор.

В основном видно, что некоторая информация хранится в сотре и может быть получена через externalKey.

Из этого в основном следует, что сеанс изначально не поддерживается сервером, а создается и управляется самой программой веб-службы.

Где он хранится? Он не обязательно должен находиться на сервере, его можно поместить в файл cookie, например, в koa-session!

Затем посмотрите на последний класс Session.

Класс сеанса

Старое правило, сначала посмотрите на конструктор:

constructor(sessionContext, obj) {
  this._sessCtx = sessionContext;
  this._ctx = sessionContext.ctx;
  if (!obj) {
    this.isNew = true;
  } else {
    for (const k in obj) {
      // restore maxAge from store
      if (k === '_maxAge') this._ctx.sessionOptions.maxAge = obj._maxAge;
      else if (k === '_session') this._ctx.sessionOptions.maxAge = 'session';
      else this[k] = obj[k];
    }
  }
}

Получил экземпляр ContextSession и передал sessionContext и obj и ничего больше.

Класс Session используется только для хранения значения сеанса и _maxAge и предоставляет метод toJSON для получения значения объекта сеанса, отфильтрованного по таким полям, как _maxAge.

Как сохранить сеанс

Прочитав приведенный выше код, мы примерно знаем, что сессия может принимать значение из внешнего файла или файла cookie, поэтому как он сохраняется, мы возвращаемся кkoa-session/index.jsупоминается вcommitметод, вы можете увидеть:

await next();

if (opts.autoCommit) {
  await sess.commit();
}

Идея сразу понятна, она в конце мидлвараnext()После этого выполняется один разcommit().

commit()метод, который может бытьlib/context.jsнайти в:

async commit() {
  // ...省略n个判断,包括是否有变更,是否需要删除session等

  await this.save(changed);
}

увидеть сноваsave()метод:

async save(changed) {
  const opts = this.opts;
  const key = opts.key;
  const externalKey = this.externalKey;
  let json = this.session.toJSON();

  // save to external store
  if (externalKey) {
    await this.store.set(externalKey, json, maxAge, {
      changed,
      rolling: opts.rolling,
    });
    if (opts.externalKey) {
      opts.externalKey.set(this.ctx, externalKey);
    } else {
      this.ctx.cookies.set(key, externalKey, opts);
    }
    return;
  }

  json = opts.encode(json);

  this.ctx.cookies.set(key, json, opts);
}

Внезапно данные json на самом деле по умолчанию помещаются в файл cookie, то есть файл cookie для хранения зашифрованной информации о сеансе.

Затем, если установлено внешнее хранилище, оно вызоветstore.set()чтобы сохранить сеанс. Конкретная логика сохранения, где его сохранять, определяется самим объектом хранилища!

резюме

Практика коа-сессии показывает, что сессия — это всего лишь объектная информация, которая может храниться в куках или где угодно (например, в памяти, базе данных). Где его хранить, разработчики могут решить сами, если реализован объект хранилища и предусмотрены методы set и get.

расширение

Благодаря приведенному выше анализу исходного кода мы получили ответы на вопросы, поставленные в начале нашей статьи.

О чем еще мы можем думать в коа-сессии?

Плагин Дизайн

Надо сказать, что плагин-дизайн магазина очень хорош. koa-session не нужно заботиться о том, как хранятся данные, пока плагин предоставляет необходимые ему методы доступа.

Эта подключаемая архитектура, инвертирующая зависимости между модулями, делает koa-session очень простым для расширения.

koa-session считает безопасность

Этот способ хранения информации о пользователе по умолчанию в файлах cookie всегда небезопасен.

Итак, теперь мы знаем, что пришло время сделать что-то еще. Например, реализовать собственный магазин, сохранить сессию в Redis и т. д.

В чем разница между этим методом входа в сеанс и токеном?

Это на самом деле зависит от того, как используется токен, и использование будет более гибким, поэтому я не буду говорить об этом здесь.

О принципах и сравнении различных стратегий входа я напишу позже, желающие могут подписаться на меня.

Суммировать

Оглядываясь назад на вопросы в начале статьи, у нас уже есть четкие ответы.

  • Сессия — это концепция, объект данных, используемый для хранения информации о посетителях.
  • Метод хранения сеанса определяется разработчиком и может храниться в памяти, Redis, MySQL или даже в файлах cookie.
  • Когда пользователь заходит в первый раз, мы создадим сеанс для пользователя и вставим его «ключ» в файл cookie. Таким образом, даже если http-запрос не имеет состояния, мы можем получить «ключ» посетителя через файл cookie, а затем мы можем получить сеанс соответствующего посетителя из коллекции сеансов всех посетителей.
  • Когда вы закрываете браузер, сессия на стороне сервера не истечет немедленно. ПО промежуточного слоя сеанса реализует набор методов управления.Когда интервал доступа превышает maxAge, сеанс становится недействительным.

Итак, помимо koa-session для входа пользователя в систему, есть ли другой способ?

На самом деле их гораздо больше, вы можете хранить реализацию куки, вы также можете использовать метод токена. Кроме того, есть единый вход, сторонний вход и т. д. Если вам интересно, вы можете продолжить анализ в следующей статье.

Категории