Подтвердить что ты не робот

График истечения срока действия сеанса

Мне часто приходилось справляться с токенами сессий/доступа, истекающими в дизайне приложений iOS, и никогда не находили на самом деле совершенно определенного дизайна, на котором я на 100% удобен, поэтому я прошу об этом здесь, чтобы узнать, может ли кто-нибудь придумать лучший дизайн, чем я сейчас использую.

Проблема

У вас есть приложение, которое регистрируется с именем пользователя и паролем. Сервер возвращает токен доступа, который должен использоваться для будущих запросов для аутентификации этого пользователя. В какой-то момент в будущем (неизвестное время) сервер истечет с этим токеном, и любой запрос, отправленный с этим токеном, вернет ошибку аутентификации.

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

Пример

Итак, представьте, что у вас есть API, чтобы получить список новостей, требующих проверки подлинности. Поток может выглядеть следующим образом:

  • Пользователь регистрируется и приложение получает токен.
  • Просмотр контроллера обновляет список новостей.
    • Запрос API выполняется с прикрепленным токеном.
    • Запрос API успешно и просмотр обновляется новыми статьями.
  • Приложение закрыто и проходит некоторое время.
  • Приложение открывается, и в этот момент контроллер просмотра хочет обновить список новостей.
    • Запрос API выполняется с прикрепленным токеном.
    • Запрос API не увенчался успехом, так как токен истек.
    • Контроллер просмотра обновляет токен и терпеливо ждет.
    • После того, как токен обновлен, запрос API повторится.

Теперь представьте, что это делается из более чем одного места в приложении.

Как я его решаю

То, что я обычно делаю, это хранить учетные данные в NSUserDefaults (если меня не интересует безопасность этих учетных данных - очевидно, лучше использовать цепочку ключей), а затем есть метод для объекта глобального менеджера (singleton), который обновляется войдите в систему, используя эти учетные данные, когда я замечаю, что сессия истекло. Этот глобальный менеджер отключает уведомления при изменении состояния входа в систему, чтобы другие части приложения могли знать, когда они должны повторить запрос после сбоя из-за истечения срока действия сеанса.

То, что я не чувствую, является правильным

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

Что я хочу знать в ответах

Я понимаю, что этот вопрос особенно расплывчатый и концептуальный (я все еще думаю, что это нормально, чтобы быть на StackOverflow, хотя?), но я просто хотел бы узнать, как другие люди решают эту проблему. Просто объясните, как вы собираетесь обрабатывать истечение сеанса, повторите неудавшиеся запросы со всего приложения и попросите пользователя ввести новые учетные данные, если обновление не работает.

Я думаю, что суть всего этого вопроса:

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

  • На уровне контроллера представления (т.е. держите флаг, чтобы сказать, что нам нужно повторить последний запрос после завершения обновления для входа в систему).
  • На уровне запроса API (т.е. инкапсулируйте запрос API в объект, который знает, чтобы повторить попытку после того, как логин был обновлен).
  • На глобальном уровне диспетчера входа (т.е., возможно, взять блок, когда вызывается refreshLogin, который выполняется после того, как логин был обновлен).
4b9b3361

Ответ 1

Не поднимать этот вопрос из мертвых, но поскольку он не ответил, я дам свой вклад.

То, что я выбрал для этого сценария, заключалось в том, чтобы хранить creds в цепочке ключей (или где угодно, на самом деле), а затем я подклассифицировал HTTPClient, чтобы проверить, обновляться или не выполняться перед каждым вызовом. Таким образом, я могу определить, что требуется обновление, выполнить его и повторить вызов за один шаг, а также при необходимости отправить блок ошибок в цепочку, если необходимо, для обработки любых случаев, когда пользователь НЕ может быть обновлен соответствующим образом.

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

РЕДАКТИРОВАТЬ: Имейте в виду, вы должны помнить, чтобы разрешить любые вызовы аутентификации!

Ответ 2

То, о чем вы просили, - как разобраться с истечением сеанса. Остальные ответили на ваш вопрос. Но я думаю, вы задаете неправильный вопрос. Позвольте мне объяснить.

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

  • Пользователь устанавливает приложение
  • Пользователь аутентифицируется с подробными сведениями и возвращается токен сеанса.
  • Затем приложение сохраняет (секретный) токен сеанса на устройстве
  • Любые будущие запросы включают токен сеанса

Заключение: Любой API, предназначенный для iOS, не должен истекать токен сеанса. Он просто скрывал на устройстве.

Таким образом, по моему опыту, ответ на ваш вопрос о шаблоне проектирования для окончания сеанса в основном: Не используйте истечение сеанса при использовании API для iOS.

Один из самых больших API-интерфейсов iOS REST делает это так, и я должен согласиться.

Ответ 3

Вы упоминаете две вещи в своем вопросе, которые кажутся ключевыми для ответа на него:

A) приложение находится в состоянии с данными, которые могут быть потеряны, если пользователь не вошел в систему.
B) приложение должно выполнить вход для сохранения данных.

Если это ограничения, вы должны обойти их соответственно:

  • Придумайте схему, в которой вы можете сохранять изменения локально. Следите за состоянием синхронизации с сервером.
  • Если изменяется статус входа в систему и что-то неожиданное, ненавязчиво предупредите пользователя и дайте ей возможность исправить его.

Ответ 4

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

Если я правильно понял ваш вопрос, у вас есть сетевой API, который обрабатывает вызовы сервера и возвращает результат (возможно, из JSON) в контроллер вида.

Лучшим подходом должно быть создание контроллера входа в систему с полями имени пользователя/электронной почты и пароля, отдельно от остальной логики приложения. После получения OK с сервера этот контроллер просмотра может быть отклонен, и приложение будет протекать по назначению.

Если ваш токен был недействителен, сервер должен вернуть ошибку 401 в свой сетевой API. Вы можете просто инкапсулировать эту ошибку в объект NSError и перейти к контроллеру представления для обработки.

В любом месте приложения серверный вызов может вернуть Error 401, и в результате вы извинитесь перед своим пользователем и откройте контроллер входа в систему, чтобы создать новый токен.

Надеюсь, я помог.

Ответ 5

Мой подход похож на ваш. (2) На уровне запроса API '- вместо того, чтобы инкапсулировать запрос в объекте, инкапсулировать всю концепцию сервера API.

Поэтому я обычно завершаю каждый тип запроса сигнатурой метода async следующим образом:

(Извините, его С#, а не obj-c (я использую Xamarin.iOS), но концепция одинаков - Action<T> эквивалентна obj-c Blocks)

void GetLatestNews(GetLatestRequest request, Action<NewsListingResponse> success, Action<Exception> error)
void Search(SearchRequest request, Action<SearchResponse> success, Action<Exception> error)

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

В любом случае, теперь ваш код клиента VC просто потребляет api по мере необходимости:

override void ViewDidAppear(bool animated)
{
    SomeNewsSiteApi.GetLatestNews( new GetLatestRequest{Count=20}, 
        response =>
        {
            // Update table view here
        },
        error =>
        {
            // Show some error alert
        });
}

Красота заключается в том, что реализация этих методов обрабатывает отправку запроса с помощью текущего токена, а если он терпит неудачу, получает новый токен, а затем повторно передает тот же запрос с новым токеном, только затем, обратный вызов Action<T> success. Клиентский код VC не имеет понятия о повторном получении токенов, все, что он знает и заботится о том, что он делает запрос на некоторые данные и ждет ответа или ошибки.

Ответ 6

Столкнувшись с той же проблемой с двух месяцев. Мой подход "(2) На уровне запроса API '

Пожалуйста, дайте мне знать, если у вас есть лучшее решение для решения этой проблемы. Это действительно поможет многим людям, которые сталкиваются с этими проблемами.

обновить токен аутентификации при завершении сеанса - очень большое решение.