繁体   English   中英

如何使用已有的REST API后端在Angular应用中实现登录系统

[英]How to implement login system in Angular app with already existing REST API backend

我和我的朋友正在构建一个应用程序-我的朋友在后端(Node.js),而我在前端。

他最终实现了会话,并为我提供了登录时需要调用的URL 。例如, POST请求

http://ourapp.heroku.com/login

用来传递usernamepassword

在我这一边,我在Angular应用程序中创建了一个登录页面,当单击“ Login时,该页面将调用Angular服务。 如果此服务从服务器收到200 ,它将执行以下操作:

$cookieStore.put(cookieNames.LOGGED_IN_COOKIE, true);
$state.go('home', {}, {reload: true});

问题是我们在前端的应用程序上遇到了奇怪的问题。 例如,登录和注销通常不起作用。 此外,用户即使注销也可以转到页面。 我发现(至少在我看来)我没有正确存储从服务器收到的Cookie,而是仅存储自己的Cookie。

整个Angular事情对我来说还是很奇怪,因为在PHP或Python应用程序中,您会从客户端收到一个页面请求,并在向他发送请求的页面之前验证他是否已登录。 在Angular中,情况有所不同-用户已经拥有所有页面。 那么,如何限制他无需登录即可看到的内容以及如何正确跟踪服务器的Cookie?

如果使用ui-router,则可以执行以下操作:

首先向您的州介绍某种访问级别

$stateProvider
        .state('admin', {
            url: "/admin",
            templateUrl: "/app/views/admin.html",
            controller: "AdminController",
            data: {
                accessLevel: 'admin'
            }
        })

那么您必须检查状态更改,如果您登录的用户具有所需的访问级别:

您可以创建一个实现您的逻辑以登录用户的身份验证服务,例如,您可以使用此服务

angular.module('app')
   .factory("AuthService", ["$rootScope", "$http", "AuthSession", "AuthHttpBuffer", "AUTH_EVENTS", function ($rootScope, $http, AuthSession, AuthHttpBuffer, AUTH_EVENTS) {

        function loginFailed() {
            $rootScope.$broadcast("auth-change", AUTH_EVENTS.loginFailed);
        };

        AuthSession.load();

        $rootScope.$on('$stateChangeStart', function (event, nextState) {
            if (nextState.data && nextState.data.accessLevel && !service.isAuthorized(nextState.data.accessLevel)) {
                event.preventDefault();
                $rootScope.$broadcast('auth-change', AUTH_EVENTS.loginRequired, nextState.name);
            }
        });

        var service = {
            login: function (credentials) {
                return $http
                            .post('/api/account/login', credentials)
                            .success(function (data, status) {
                                if ((status < 200 || status >= 300) && data.length >= 1) {
                                    loginFailed();
                                    return;
                                }

                                AuthSession.create(data.AccessToken, data.User);
                                $rootScope.$broadcast("auth-change", AUTH_EVENTS.loginSuccess);
                                AuthHttpBuffer.retryAll();
                            }).error(function (data, status) {
                                loginFailed();
                            });
            },
            cancel: function () {
                AuthHttpBuffer.rejectAll();
            },
            logout: function () {
                AuthSession.destroy();
                $rootScope.$broadcast("auth-change", AUTH_EVENTS.logoutSuccess);
            },
            isAuthenticated: function () {
                return (AuthSession.token !== null);
            },
            isAuthorized: function (accessLevel) {
                if (!accessLevel) return true;

                return (this.isAuthenticated() && AuthSession.user.UserRoles.indexOf(accessLevel) !== -1);
            }

        }
        return service;
    }]);

和您的AuthSession服务:

angular.module('app')
      .factory("AuthSession", ["$rootScope", "$window", "AUTH_EVENTS", function ($rootScope, $window, AUTH_EVENTS) {

        var sessionService = {
            user: null,
            token: null,

            //load the stored session data
            load: function () {
                var user = ...yourdata... //TODO implement load user data;
                var token = ...yourdata... //implement load user data;

                if (!user || !token) return;

                if (!this.checkTokenExpiration(token)) return;

                this.user = user;
                this.token = token;

                $rootScope.$broadcast("auth-change", AUTH_EVENTS.loginSuccess);
            },

            //save the current data to the session storage
            save: function () {
                //TODO save your userdata/token etc.
            },

            //create the current user with the assosiated token
            create: function (token, user) {
                this.token = token;
                this.user = user;

                if (!angular.isArray(this.user.UserRoles))
                    this.user.UserRoles = [this.user.UserRoles];

                this.save();
            },

            //destroy an user with all assosiated data
            destroy: function () {
                this.token = null;
                this.user = null;

                //TODO clear your saved data here
            },

            //check if the supplied access token data is expired
            checkTokenExpiration: function (token) {
                if (token === undefined || token === null) return false;

                var retval = (new Date(token.TokenExpires).getTime() > new Date().getTime());

                if (retval === false) {
                    sessionService.destroy();
                    $rootScope.$broadcast("auth-change", AUTH_EVENTS.sessionTimeout);
                }

                return retval;
            }
        }

        return sessionService;

      }]);

和常数:

angular.module('app')
      .constant('AUTH_EVENTS', {
        loginSuccess: 'auth-login-success',
        loginFailed: 'auth-login-failed',
        logoutSuccess: 'auth-logout-success',
        loginRequired: 'auth-login-required',
        sessionTimeout: 'auth-session-timeout',
        notAuthorized: 'auth-not-authorized'
      });

如果您希望捕获没有正确访问权限的网址,则可以将请求发送到http缓冲区:

angular.module('app')
      .factory('AuthHttpBuffer', ["$injector", function ($injector) {
        /** Holds all the requests, so they can be re-requested in future. */
        var buffer = [];

        /** Service initialized later because of circular dependency problem. */
        var $http;

        function retryHttpRequest(config, deferred) {
            function successCallback(response) {
                deferred.resolve(response);
            }
            function errorCallback(response) {
                deferred.reject(response);
            }
            $http = $http || $injector.get('$http');
            $http(config).then(successCallback, errorCallback);
        }

        return {
            /**
                 * Appends HTTP request configuration object with deferred response attached to buffer.
                 */
            append: function (config, deferred) {
                buffer.push({
                    config: config,
                    deferred: deferred
                });
            },

            /**
                 * Abandon or reject (if reason provided) all the buffered requests.
                 */
            rejectAll: function (reason) {
                if (reason) {
                    for (var i = 0; i < buffer.length; ++i) {
                        buffer[i].deferred.reject(reason);
                    }
                }
                buffer = [];
            },

            /**
                 * Retries all the buffered requests clears the buffer.
                 */
            retryAll: function () {
                for (var i = 0; i < buffer.length; ++i) {
                    retryHttpRequest(buffer[i].config, buffer[i].deferred);
                }
                buffer = [];
            }
        };
      }]);

如果还不够,还可以添加一个拦截器,如果服务器响应未经授权,该拦截器会触发auth change事件:

    angular.module('app')
      .factory('AuthInterceptor', ["$rootScope", "$q", "AuthSession", "AuthHttpBuffer", "AUTH_EVENTS", function ($rootScope, $q, AuthSession, AuthHttpBuffer, AUTH_EVENTS) {

        return {
            request: function (config) {
                config.headers = config.headers || {};
                if (AuthSession.token) {
                    config.headers.Authorization = 'Bearer ' + AuthSession.token.TokenKey;
                }
                return config;
            },
            responseError: function (rejection) {
                if (rejection.status === 401) {
                    var deferred = $q.defer();

                    AuthHttpBuffer.append(rejection.config, deferred);

                    if (AuthSession.token) {
                        $rootScope.$broadcast('auth-change', AUTH_EVENTS.notAuthorized);
                    } else {
                        $rootScope.$broadcast('auth-change', AUTH_EVENTS.loginRequired);
                    }
                    return deferred.promise;
                }
                return $q.reject(rejection);
            }
        }
      }]);

该拦截器还将会话令牌添加到所有请求(如果可用)。

要使用此拦截器,必须将以下两行添加到app.config()中:

    $httpProvider.defaults.withCredentials = true;
    $httpProvider.interceptors.push("AuthInterceptor");

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM