Страница перенаправления страницы входа в Durandal
TL; DR Что такое хороший шаблон, требующий от пользователя входа в систему для просмотра определенных страниц в приложении Durandal Single Page Application (SPA)?
Мне нужна система, в которой, если пользователь пытается перейти на "страницу", которая требует, чтобы они вошли в систему, они вместо этого перенаправляются на страницу входа. После успешной аутентификации на этой странице входа мне хотелось бы, чтобы приложение перенаправляло их на страницу, к которой они ранее пытались получить доступ, прежде чем они были перенаправлены на страницу входа.
Один метод, который я могу представить, который может быть реализован для перенаправления пользователя на страницу входа и из нее, - это сохранить URL-адрес страницы, которая требует аутентификации в истории браузера, и после успешной проверки подлинности вернитесь к этой истории на странице входа.
Реализация
Я попытался реализовать описанный выше шаблон, но с ним несколько проблем. В main.js
(или где-либо перед тем, как router.activate()
вызывается) я защищаю маршрут, требующий проверки подлинности:
router.guardRoute = function (instance, instruction) {
if (user.isAuthenticated()) {
return true;
} else {
if (instance && typeof (instance.allowAnonymous) === "boolean") {
if (!instance.allowAnonymous) {
/* Use one of the following two methods to store an item in the
browser history. */
/* Add guarded URL to the history and redirect to login page.
* We will navigate back to this URL after a successful login.
*/
if (history.pushState) {
history.pushState(null, null, '#' + instruction.fragment);
}
else {
location.hash = '#' + instruction.fragment;
}
*/
/* This will not work - history is not updated if the fragment
* matches the current fragment.*/
////router.navigate(instruction.fragment, false);
/* The following solution puts in a fragment to the history
* that we do not want the user to see. Is this an
* acceptable solution? */
router.navigate(instruction.fragment + 'LoginRedirect', false);
return router.convertRouteToHash("login");
}
}
return true;
}
};
В shell.js
я добавляю маршрут на страницу входа:
return {
router: router,
activate: function () {
router.map([
...
{ route: 'login', title: '', moduleId: 'viewmodels/login', nav: true },
...
]).buildNavigationModel();
return router.activate();
},
...
}
В viewmodels/login.js
Затем у меня есть фрагмент кода:
if (account.isAuthenticated()) {
router.navigateBack();
}
Ограничения
Одно из ограничений этого метода заключается в том, что он позволяет пользователю перейти на страницу входа в систему после того, как они прошли аутентификацию и перенаправлены с страницы входа.
Кроме того, используя
router.navigate(instruction.fragment + 'LoginRedirect', false);
Я иногда вижу, как LoginRedirect
вспыхивает в URL-адресе.
Более серьезным ограничением является то, что пользователь, возвращающийся на страницу входа, не будет перенесен на предыдущую (не охраняемую) страницу (но вместо этого будет отправлен на охраняемую страницу, которая будет перенаправляться на страницу входа).
Также кажется, что есть ошибка с обновленной страницей guardRoute
, позволяющей обновить страницу (см. здесь. Это еще проблема?
Являются ли они стандартными шаблонами Дюрандала, которые инкапсулируют вышеуказанное поведение и не имеют аналогичных ограничений, перечисленных выше?
Ответы
Ответ 1
Шаблон строки запроса
Попытка реализовать метод "вернуться назад", описанный в моем вопросе, я решил не использовать этот метод из-за ограничений, описанных выше.
Я нашел на практике, что метод, который работает намного лучше, - это передать URL-адрес на страницу, которая требует аутентификации в качестве строки запроса на странице входа, которая затем может использовать ее для перехода по этому URL-адресу после того, как пользователь проверку подлинности.
Ниже приводится схема реализации этого метода, который я принял. Я все еще хочу узнать о любых других шаблонах входа, которые люди использовали для использования в приложениях Durandal.
Реализация
В main.js
(или где-либо перед тем, как router.activate()
вызывается) я по-прежнему защищаю маршрут, требующий проверки подлинности:
router.guardRoute = function (instance, instruction) {
if (user.isAuthenticated()) {
return true;
} else {
if (instance && typeof (instance.preventAnonymous) === "boolean") {
if (instance.preventAnonymous) {
return 'login/' + instruction.fragment;
}
}
return true;
}
};
В shell.js
:
return {
router: router,
activate: function () {
router.map([
...
{ route: 'login', title: '', moduleId: 'viewmodels/login', nav: true },
{ route: 'login/:redirect', title: '', moduleId: 'viewmodels/login', nav: true },
...
]).buildNavigationModel();
return router.activate();
},
...
}
В viewmodels/login.js
:
viewmodel = {
...,
canActivate: function() {
if (!user.isAuthenticated()) {
return true;
}
return false;
},
activate: function(redirect) {
viewmodel.redirect = redirect || "";
},
loginUser() {
...
if (user.isAuthenticated()) {
router.navigate(viewmodel.redirect);
}
...
},
...
}
Ограничение
Одним из незначительных недостатков этого метода является сжатие фрагмента страницы для перенаправления на успешный вход в строку запроса URL-адреса приложения. Если вам не нравится появление этого фрагмента страницы в строке запроса URL, локальное хранилище может быть легко использовано для хранения этого значения. В router.guardRoute
можно просто заменить строку
return 'login/' + instruction.fragment;
с
/* Check for local storage, cf. http://diveintohtml5.info/storage.html */
if ('localStorage' in window && window['localStorage'] !== null){
localStorage.setItem("redirect", instruction.fragment);
return 'login/';
} else {
return 'login/' + instruction.fragment;
}
и наш метод activate
может выглядеть так:
activate: function(redirect) {
if ('localStorage' in window && window['localStorage'] !== null){
viewmodel.redirect = localStorage.getItem("redirect");
} else {
viewmodel.redirect = redirect || "";
}
},
Ответ 2
Мой метод:
Я разработал базовую функцию модели представления, которая является основой для всех моделей представления моего приложения. В методе canActivate
(в базе данных модели) я вернусь false
и перейду на страницу входа, если у текущего пользователя нет разрешения на просмотр этой страницы. Я также выполняю запрос к базе данных на стороне клиента (к определенному пользователю имеет доступ к странице или нет).
Хорошей новостью является то, что Durandal поддерживает отложенное выполнение для canActivate
. На странице входа в систему после успешного входа в систему я использую navigator.goBack();
для возврата на предыдущую страницу.
Надеюсь, это поможет вам.