16

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在 URL 中看到闪现。

一个更严重的限制是用户在登录页面上按回不会被带到前一个(非保护)页面(而是会被带到保护页面,该页面将重定向到登录页面)。

guardRoute刷新页面似乎也存在错误(请参见此处。这仍然是一个问题吗?

它们是否包含任何标准的 Durandal 模式,这些模式封装了上述行为并且没有与上面列出的类似限制?

4

2 回答 2

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 || "";
    }
},
于 2013-10-15T12:14:45.113 回答
1

我的方法:

我开发了一个视图模型基础函数,它是我的应用程序所有视图模型的基础。在方法中(在视图模型库中),如果当前用户无权查看该页面canActivate,我将返回并导航到登录页面。false我还对客户端数据库执行查询(以确定用户是否可以访问页面)。

好消息是 Durandal 支持canActivate. 在登录页面,登录成功后,我将使用navigator.goBack();返回到上一页。

我希望这可以帮助你。

于 2013-09-08T02:53:14.540 回答