График истечения срока действия сеанса
Мне часто приходилось справляться с токенами сессий/доступа, истекающими в дизайне приложений iOS, и никогда не находили на самом деле совершенно определенного дизайна, на котором я на 100% удобен, поэтому я прошу об этом здесь, чтобы узнать, может ли кто-нибудь придумать лучший дизайн, чем я сейчас использую.
Проблема
У вас есть приложение, которое регистрируется с именем пользователя и паролем. Сервер возвращает токен доступа, который должен использоваться для будущих запросов для аутентификации этого пользователя. В какой-то момент в будущем (неизвестное время) сервер истечет с этим токеном, и любой запрос, отправленный с этим токеном, вернет ошибку аутентификации.
После сбоя из-за истечения срока действия сеанса приложение должно повторно войти в систему, используя оригинальные учетные данные, и вернуть новый токен доступа. Затем он может повторить исходный запрос.
Пример
Итак, представьте, что у вас есть API, чтобы получить список новостей, требующих проверки подлинности. Поток может выглядеть следующим образом:
- Пользователь регистрируется и приложение получает токен.
- Просмотр контроллера обновляет список новостей.
- Запрос API выполняется с прикрепленным токеном.
- Запрос API успешно и просмотр обновляется новыми статьями.
- Приложение закрыто и проходит некоторое время.
- Приложение открывается, и в этот момент контроллер просмотра хочет обновить список новостей.
- Запрос API выполняется с прикрепленным токеном.
- Запрос API не увенчался успехом, так как токен истек.
- Контроллер просмотра обновляет токен и терпеливо ждет.
- После того, как токен обновлен, запрос API повторится.
Теперь представьте, что это делается из более чем одного места в приложении.
Как я его решаю
То, что я обычно делаю, это хранить учетные данные в NSUserDefaults
(если меня не интересует безопасность этих учетных данных - очевидно, лучше использовать цепочку ключей), а затем есть метод для объекта глобального менеджера (singleton), который обновляется войдите в систему, используя эти учетные данные, когда я замечаю, что сессия истекло. Этот глобальный менеджер отключает уведомления при изменении состояния входа в систему, чтобы другие части приложения могли знать, когда они должны повторить запрос после сбоя из-за истечения срока действия сеанса.
То, что я не чувствую, является правильным
Ну, мне просто никогда не нравилась обработка конечной машины объекта-менеджера. Каждое место, которое выполняет запрос, должно сохранить некоторое состояние, чтобы знать, что обновление для входа происходит и повторить запрос после того, как логин был обновлен. Там также проблема о том, что делать, если обновление завершается неудачно, потому что пароль сейчас не так (пользователь изменил его, возможно) - вы, вероятно, не хотите полностью выходить из системы и уничтожать все пользовательское состояние приложения, потому что вы можете просто быть в состоянии запросить новый пароль и продолжить работу по-прежнему. Но глобальный менеджер на самом деле не связан с пользовательским интерфейсом, поэтому ему сложно обрабатывать пользовательский интерфейс, запрашивая новый логин.
Что я хочу знать в ответах
Я понимаю, что этот вопрос особенно расплывчатый и концептуальный (я все еще думаю, что это нормально, чтобы быть на StackOverflow, хотя?), но я просто хотел бы узнать, как другие люди решают эту проблему. Просто объясните, как вы собираетесь обрабатывать истечение сеанса, повторите неудавшиеся запросы со всего приложения и попросите пользователя ввести новые учетные данные, если обновление не работает.
Я думаю, что суть всего этого вопроса:
Где поставить логику повторения запросов, которые не удались из-за истечения сеанса. Я вижу, что эти места есть варианты:
- На уровне контроллера представления (т.е. держите флаг, чтобы сказать, что нам нужно повторить последний запрос после завершения обновления для входа в систему).
- На уровне запроса API (т.е. инкапсулируйте запрос API в объект, который знает, чтобы повторить попытку после того, как логин был обновлен).
- На глобальном уровне диспетчера входа (т.е., возможно, взять блок, когда вызывается
refreshLogin
, который выполняется после того, как логин был обновлен).
Ответы
Ответ 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 '
Пожалуйста, дайте мне знать, если у вас есть лучшее решение для решения этой проблемы.
Это действительно поможет многим людям, которые сталкиваются с этими проблемами.
обновить токен аутентификации при завершении сеанса - очень большое решение.