Durandal登录页面重定向模式

Chr*_*ris 15 javascript authentication login durandal

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;
    }
};
Run Code Online (Sandbox Code Playgroud)

shell.js我添加到登录页面的路由:

return {
    router: router,
    activate: function () {
        router.map([
        ...
        { route: 'login', title: '', moduleId: 'viewmodels/login', nav: true },
        ...
        ]).buildNavigationModel();
        return router.activate();
    },
    ...
}
Run Code Online (Sandbox Code Playgroud)

viewmodels/login.js我然后有代码片段:

if (account.isAuthenticated()) {
    router.navigateBack();
} 
Run Code Online (Sandbox Code Playgroud)

限制

此方法的一个限制是,它允许用户在经过身份验证并从登录页面重定向后向前导航到登录页面.

此外,使用

router.navigate(instruction.fragment + 'LoginRedirect', false);
Run Code Online (Sandbox Code Playgroud)

我偶尔会LoginRedirect在URL中看到闪烁.

更严重的限制是用户按下登录页面将不会被带到之前的(未受保护的)页面(而是将被带到受保护的页面,该页面将重定向到登录页面).

似乎guardRoute刷新的页面有一个错误(见这里.这仍然是个问题吗?

它们是否包含上述行为的标准Durandal模式,并没有上面列出的类似限制?

Chr*_*ris 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;
    }
};
Run Code Online (Sandbox Code Playgroud)

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();
    },
    ...
}
Run Code Online (Sandbox Code Playgroud)

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);
        }
        ...
    },
    ...
}
Run Code Online (Sandbox Code Playgroud)

局限性

此方法的一个小的负面因素是页面片段在应用程序URL查询字符串中成功登录时重定向的前提.如果您不喜欢URL查询字符串中此页面片段的存在,则可以轻松地使用本地存储来存储此值.在router.guardRoute一个可以简单的替换线

return 'login/' + instruction.fragment;
Run Code Online (Sandbox Code Playgroud)

/* 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;
}
Run Code Online (Sandbox Code Playgroud)

我们的activate方法可能如下所示:

activate: function(redirect) {
    if ('localStorage' in window && window['localStorage'] !== null){
        viewmodel.redirect = localStorage.getItem("redirect");
    } else {
        viewmodel.redirect = redirect || "";
    }
},
Run Code Online (Sandbox Code Playgroud)

  • 这对我有用,但要确保如果你把片段放在路径中,它的格式为`login*fragement`而不是`login(/:fragement)`,否则你会得到查看未找到的问题像`#/ login/user/5`这样的路径.另外,`router.navigate(fragement,{replace:true,trigger:true});`将用新位置替换历史记录,因此您无法返回登录页面. (2认同)