Javascript 在Angular中初始时间进行用户身份验证的最佳实践/模式?

Javascript 在Angular中初始时间进行用户身份验证的最佳实践/模式?,javascript,angularjs,authentication,Javascript,Angularjs,Authentication,我正在为Angular SPA构建登录功能。我的所有登录/注销功能都封装在一个名为AuthService的服务中(反过来,它封装了一些$resources)。如果用户的浏览器具有有效的PHP会话cookie,则认为该用户已“登录” 除了一件事,我的一切都正常工作:在初始化时确定用户是否已经登录 目前,我的代码如下所示: angular.module( 'MyModule', [ 'ngResource', 'ngRoute' ] ) .run( [ '$rootScope', '$loc

我正在为Angular SPA构建登录功能。我的所有登录/注销功能都封装在一个名为AuthService的服务中(反过来,它封装了一些$resources)。如果用户的浏览器具有有效的PHP会话cookie,则认为该用户已“登录”

除了一件事,我的一切都正常工作:在初始化时确定用户是否已经登录

目前,我的代码如下所示:

angular.module( 'MyModule', [ 'ngResource', 'ngRoute' ] )
    .run( [ '$rootScope', '$location', 'AuthService', function( $rootScope, $location, AuthService ) {

        /**
         * Initialize AuthService. This will cause it to make a REST call
         * to GET /api/current_user. That endpoint will return either a JSON
         * representation of the logged-in user, or a 401 Unauthorized.
         * Either way, AuthService will cache the results, which can be
         * accessed by calling AuthService.getUser().
         */
        AuthService.init();

        /**
         * Listen for route changes. If the user tries to change to a route
         * that requires login, but the user isn't logged in, then redirect
         * to the login page.
         */
        $rootScope.$on( '$routeChangeStart', function( event, next, current ) {

            if ( next.$$route.requiresLogin && AuthService.getUser() === null ) {
                $location.path( '/login' );
            }

        } );

    } ] );
<script src="MyModule.min.js"></script>
<script src="AuthService.js.php"></script>
显然,这里有一个问题:对
AuthService.init()
的调用需要时间才能完成(因为它必须进行HTTP调用),但第一个
$routeChangeStart
事件将在
.run()
完成后立即触发

因此,即使用户已经登录,
AuthService
也不会知道,用户将被“重定向”到登录页面

我可以调整AuthService.init()以返回承诺,并相应地重构此代码:

angular.module( 'MyModule', [ 'ngResource', 'ngRoute' ] )
    .run( [ '$rootScope', '$location', '$route', 'AuthService', function( $rootScope, $location, $route, AuthService ) {

        /**
         * Initialize AuthService. This will cause it to make a REST call
         * to GET /api/current_user. That endpoint will return either a JSON
         * representation of the logged-in user, or a 401 Unauthorized.
         * Either way, AuthService will cache the results, which can be
         * accessed by calling AuthService.getUser().
         */
        AuthService.init(
            function() {

                // Make sure the user is allowed to view the initial route
                enforce_login( $route.current );

                // Also check when the user tries to navigate to another route
                $rootScope.$on( '$routeChangeStart', function( event, next, current ) {
                    enforce_login( next );
                } );

            }
        );

        function enforce_login( theRoute ) {
            if ( theRoute.$$route.requiresLogin && AuthService.getUser() === null ) {
                $location.path( '/login' );
            }
        }

    } ] );
但是,这也有问题:如果对
GET/api/current_user
的HTTP调用需要一两秒钟才能完成,并且用户已经加载了受保护的路由,那么应用程序将在重定向到登录页面之前短暂显示受保护的路由

我真正需要做的是延迟加载/呈现初始路由,直到AuthService有机会确定用户的登录状态

有解决这个问题的典型方法吗?我看到的所有Angular authentication教程都假设您的AuthService能够同步确定用户的登录状态。但他们并没有真正解释它是如何做到这一点的

一种解决方案是首先消除进行异步调用的需要。例如,我可以从单独的
标记加载AuthService,如下所示:

angular.module( 'MyModule', [ 'ngResource', 'ngRoute' ] )
    .run( [ '$rootScope', '$location', 'AuthService', function( $rootScope, $location, AuthService ) {

        /**
         * Initialize AuthService. This will cause it to make a REST call
         * to GET /api/current_user. That endpoint will return either a JSON
         * representation of the logged-in user, or a 401 Unauthorized.
         * Either way, AuthService will cache the results, which can be
         * accessed by calling AuthService.getUser().
         */
        AuthService.init();

        /**
         * Listen for route changes. If the user tries to change to a route
         * that requires login, but the user isn't logged in, then redirect
         * to the login page.
         */
        $rootScope.$on( '$routeChangeStart', function( event, next, current ) {

            if ( next.$$route.requiresLogin && AuthService.getUser() === null ) {
                $location.path( '/login' );
            }

        } );

    } ] );
<script src="MyModule.min.js"></script>
<script src="AuthService.js.php"></script>

…和
AuthService.js.php
将动态生成一个js文件,该文件在初始化时已经知道用户是否登录

然而,这感觉真的很粗糙——它将客户端应用程序与服务器端实现相结合,使AuthService.js不可缓存,并且通常是一个难题

因此,我的问题是:在Angular计算并呈现初始路由之前,如何最好地在Angular初始时间检查用户的登录状态

resolve: { AuthService: 'AuthService' }
(或解析任何从身份验证服务返回承诺的内容)作为所有访问控制路由的依赖项,请参阅

侦听以重定向到登录路径。

使用

resolve: { AuthService: 'AuthService' }
(或解析任何从身份验证服务返回承诺的内容)作为所有访问控制路由的依赖项,请参阅


请注意重定向到登录路径。

这对我来说很有效;谢谢对于未来的读者:这种方法根本不使用.run()。相反,它使用ngRoute的.when()方法的“resolve”选项。在Angular转向该路线之前,必须满足向“resolve”传递的任何承诺。因此,它是进行身份验证检查等操作的完美场所。作为奖励,这使得授权检查天生就是延迟加载:在用户实际尝试切换到受保护的路由之前,它不会进行REST调用。没错,这就是具有全局init依赖项的应用程序使用路由器的主要原因,即使它不太使用路由。控制器中不再有承诺意大利面。这对我很有效;谢谢对于未来的读者:这种方法根本不使用.run()。相反,它使用ngRoute的.when()方法的“resolve”选项。在Angular转向该路线之前,必须满足向“resolve”传递的任何承诺。因此,它是进行身份验证检查等操作的完美场所。作为奖励,这使得授权检查天生就是延迟加载:在用户实际尝试切换到受保护的路由之前,它不会进行REST调用。没错,这就是具有全局init依赖项的应用程序使用路由器的主要原因,即使它不太使用路由。不要再在控制器里放意大利面条了。