TL; DR 要求用户登录以查看Durandal单页应用程序(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
闪现。
更严重的限制是,用户按下登录页面将不会被带到上一个(未受保护的)页面(而是将被带到受保护的页面,该页面将重定向到登录页面)。
似乎还有一个错误,guardRoute
允许页面刷新(请参阅here。这仍然是个问题吗?
它们是否包含上述行为的标准Durandal模式,并没有上面列出的类似限制?
答案 0 :(得分:6)
尝试实施"导航回"在我的问题中描述的方法我决定不使用这种方法,因为我已经描述了上述限制。
我在实践中发现,一种效果更好的方法是将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 || "";
}
},
答案 1 :(得分:1)
我的方法:
我开发了一个视图模型基本函数,它是我的应用程序的所有视图模型的基础。在canActivate
方法(在视图模型库中),如果当前用户没有查看该页面的权限,我将返回false
并导航到登录页面。我还对客户端数据库执行查询(以确定用户是否可以访问页面)。
好消息是Durandal支持canActivate
的延期执行。在登录页面中,成功登录后,我将使用navigator.goBack();
返回上一页。
我希望这会对你有所帮助。