【问题标题】:Why is AngularJS service re-initialized when route is re-loaded?为什么重新加载路由时重新初始化AngularJS服务?
【发布时间】:2016-01-18 03:27:16
【问题描述】:

AngularJS 服务被注入到两个独立的模块中。这导致服务在第二个模块调用它时单独重新初始化。我已使用 FireFox 调试器确认模块正在重新初始化。我怎样才能避免这个问题?

这里是具体案例:

AngularJS 应用使用名为auth 的模块中的身份验证服务来管理身份验证问题。 auth 服务被导入到 message 模块中,该模块管理对安全 /message 路由的访问,auth 也被导入到 navigation 模块中,该模块管理登录/注册以及导航的可见内容用户浏览器中的链接。用户能够使用链接到navigation 模块的GUI 工具成功登录,然后作为经过身份验证的用户成功重定向到安全的/message 路由,因为auth.authenticated1auth.authenticated2 属性设置为@987654331 @ 在重定向到 /message 之前发生。

然而,FireFox 调试器确认问题在于,当用户刷新浏览器以重新加载/message url 模式时,auth 模块被重新初始化,设置了auth.authenticated1 和@987654336 的值@ 返回 false,从而向用户显示他们未登录的消息,即使他们在使用用户提供的有效凭据之前已登录。 需要对下面的代码进行哪些具体更改,以便用户不会在页面重新加载时注销?

我希望 AngularJS 代码在 /message 路由被加载或重新加载时检查 auth.authenticated2 的预先存在的值。如果是 auth.authenticated2=false,那么用户会收到一条消息说他们被注销。但是如果auth.authenticated2=true,我希望用户能够在/message 路由上看到安全内容。 我不希望 auth.authenticated2 在重新加载路线时自动重新设置为 false,就像现在这样。

这是message.html 中的代码,其中包含/message 路由的GUI 元素:

<div ng-show="authenticated2()">
    <h1>Secure Content</h1>
    <div>
        <p>Secure content served up from the server using REST apis for authenticated users.</p>
    </div>
</div>
<div ng-show="!authenticated2()">
    <h1>You are not logged in.</h1>
</div>

这是message.js 中的代码,它是管理/message 路由的message 模块的控制器:

angular.module('message', ['auth']).controller('message', function($scope, $http, $sce, auth) {

    $scope.authenticated2 = function() {
        return auth.authenticated2;
    }

    //Other code calling REST apis from the server omitted here to stay on topic

});

这是navigation 模块的代码,它也注入了auth 服务:

angular.module('navigation', ['ngRoute', 'auth']).controller('navigation', function($scope, $route, auth, $http, $routeParams, $location) {

    $scope.credentials = {};//from old navigation module
    $scope.leadresult = "blank";
    $scope.processStep = "start";
    $scope.uname = "blank";
    $scope.wleadid = "initial blank value";
    $scope.existing = "blank";

    $scope.tab = function(route) {
        return $route.current && route === $route.current.controller;
    };

    $scope.authenticated1 = function() {
        return auth.authenticated1;
    }

    $scope.authenticated2 = function() {
        return auth.authenticated2;
    }

    $scope.login = function() {
        auth.authenticate1($scope.credentials, function(authenticated1) {
            //a bunch of stuff that does level 1 authentication, which is not relevant here
        })
    }

    $scope.logout = auth.clear;

    //some other methods to manage registration forms in a user registration process, which are omitted here because they are off-topic

    $scope.pinForm = function(isValid) {//this method finishes authentication of user at login
        if (isValid) {
            $scope.resultmessage.webleadid = $scope.wleadid;
            $scope.resultmessage.name = $scope.uname;
            $scope.resultmessage.existing = $scope.existing;
            var funcJSON = $scope.resultmessage;        
            auth.authenticate2(funcJSON, function(authenticated2) {
                if (authenticated2) {
                    $location.path('/message');
                    $scope.$apply();//this line successfully re-directs user to `/message` route LOGGED IN with valid credentials
                }
            });
        }
    };

    $scope.$on('$viewContentLoaded', function() {
        //method that makes an unrelated call to a REST service for ANONYMOUS users
    });

});

这是auth.jsauth服务的代码:

angular.module('auth', []).factory( 'auth', function($rootScope, $http, $location) {

    var auth = {

        authenticated1 : false,
        authenticated2 : false,
        usrname : '',

        loginPath : '/login',
        logoutPath : '/logout',
        homePath : '/message',
        path : $location.path(),

        authenticate1 : function(credentials, callback) {
            var headers = credentials && credentials.username ? {
                authorization : "Basic " + btoa(credentials.username + ":" + credentials.password)
            } : {};

            $http.get('user', {
                headers : headers
            }).success(function(data) {
                if (data.name) { auth.authenticated1 = true; } 
                else { auth.authenticated1 = false; }
                callback && callback(auth.authenticated1);
            }).error(function() {
                auth.authenticated1 = false;
                callback && callback(false);
            });
        },

        authenticate2 : function(funcJSON, callback) {
            $http.post('/check-pin', funcJSON).then(function(response) {
                if(response.data.content=='pinsuccess'){
                    auth.authenticated2=true;
                    callback && callback(auth.authenticated2);
                }else {
                    auth.authenticated2=false;
                    auth.authenticated2 = false;
                    callback && callback(false);
                }
            });
        },

        clear : function() {
            $location.path(auth.loginPath);
            auth.authenticated1 = false;
            auth.authenticated2 = false;
            $http.post(auth.logoutPath, {}).success(function() { console.log("Logout succeeded");
            }).error(function(data) { console.log("Logout failed"); });
        },

        init : function(homePath, loginPath, logoutPath) {
            auth.homePath = homePath;
            auth.loginPath = loginPath;
            auth.logoutPath = logoutPath;
        }
    };
    return auth;
});

routeProvider 由应用程序的主js 文件管理,即hello.js,如下所示:

angular.module('hello', [ 'ngRoute', 'auth', 'home', 'message', 'public1', 'navigation' ])
    .config(

        function($routeProvider, $httpProvider, $locationProvider) {

            $locationProvider.html5Mode(true);/* This line is one of 3 places where we set natural urls to remote the default # */

            $routeProvider.when('/', {
                templateUrl : 'js/home/home.html',
                controller : 'home'
            }).when('/message', {
                templateUrl : 'js/message/message.html',
                controller : 'message'
            }).when('/public1', {
                templateUrl : 'js/public1/public1.html',
                controller : 'public1'
            }).when('/register', {
                templateUrl : 'js/navigation/register.html',
                controller : 'navigation'
            }).otherwise('/');

            $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

        }).run(function(auth) {

            // Initialize auth module with the home page and login/logout path
            // respectively
            auth.init('/checkpin', '/login', '/logout');

        });

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

    不是一个完整的答案,如在此处更改的确切代码。但足以让一些东西构建得很好。

    您不应在身份验证服务中显式仅使用布尔值。据我所知,您没有使用成功身份验证后从服务器接收的任何数据。因此,每当您刷新时,一切都会丢失。您的代码中没有任何内容可以从刷新中恢复。

    理想情况下,您应该拥有令牌或 cookie 之类的东西。 cookie 会被保存,刷新后可以恢复,所以在启动 auth 服务时,可以检查该 cookie 是否存在。

    您还可以保存一个令牌,该令牌可用于访问 indexedDB 内的 API 或类似的东西。正如我之前所说,在身份验证服务启动期间,您必须检查该令牌是否存在。

    如果您需要更多信息,请检查 Oauth2。即使 Oauth2 不是身份验证 API 而是授权 API,您也可以使用密码授予类型。了解如何构建一个可靠的系统。其他授权类型大多只关注 OAuth 的授权方面。

    因为 OP 在这里要求代码:

    when creating_service:
      if exists(cookie) or exists(token) and valid(cookie) or valid(token):
         mark_service_as_logged_in()
    

    如果伪代码比文字更好。

    【讨论】:

    • @CodeMed 您要求他为您编写一个身份验证系统。这不是一项小任务。它完全与您的服务器相关联。您可能会考虑对 cookie/身份验证令牌进行更多研究,然后在遇到困难时提出更窄的问题。
    • 哇...Please show code. The OP asks for code希望你在开玩笑吧?
    • cookie 必须在服务器端完成。很抱歉,您要求的是理论上的答案,因为您似乎不知道身份验证的工作原理。
    • 此用户的回复并未开始解决 OP 中所述的问题。
    • OP 明确规定:需要对下面的代码进行哪些具体更改,以便用户不会在页面重新加载时注销?
    猜你喜欢
    • 2015-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-26
    • 2012-06-18
    相关资源
    最近更新 更多