AngularJS-每个路由和控制器中的登录和身份验证

iCo*_*ode 129 javascript ruby-on-rails angularjs angularjs-authentication

我有一个使用yeoman,grunt和bower创建的AngularJS应用程序.

我有一个登录页面,其中包含一个检查身份验证的控制器.如果凭据正确,我将重新路由到主页.

app.js

'use strict';
//Define Routing for app
angular.module('myApp', []).config(['$routeProvider', '$locationProvider',
  function($routeProvider,$locationProvider) {
    $routeProvider
    .when('/login', {
        templateUrl: 'login.html',
        controller: 'LoginController'
    })
    .when('/register', {
        templateUrl: 'register.html',
        controller: 'RegisterController'
      })
    .when('/forgotPassword', {
        templateUrl: 'forgotpassword.html',
        controller: 'forgotController'
      })
   .when('/home', {
       templateUrl: 'views/home.html',
       controller: 'homeController'
    })
    .otherwise({
       redirectTo: '/login'
    });
//    $locationProvider.html5Mode(true); //Remove the '#' from URL.
}]);

angular.module('myApp').factory("page", function($rootScope){
    var page={};
    var user={};
    page.setPage=function(title,bodyClass){
        $rootScope.pageTitle = title;
        $rootScope.bodylayout=bodyClass;
    };
    page.setUser=function(user){
        $rootScope.user=user;
    }
    return page;
});
Run Code Online (Sandbox Code Playgroud)

LoginControler.js

'use strict';

angular.module('myApp').controller('LoginController', function($scope, $location, $window,page) {
    page.setPage("Login","login-layout");
    $scope.user = {};
    $scope.loginUser=function()
    {
        var username=$scope.user.name;
        var password=$scope.user.password;
        if(username=="admin" && password=="admin123")
        {
            page.setUser($scope.user);
            $location.path( "/home" );
        }
        else
        {
            $scope.message="Error";
            $scope.messagecolor="alert alert-danger";
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

在我的主页上

<span class="user-info">
    <small>Welcome,</small>
    {{user.name}}
</span>
<span class="logout"><a href="" ng-click="logoutUser()">Logout</a></span>
Run Code Online (Sandbox Code Playgroud)

loginController,我检查登录信息,如果成功,我在服务工厂设置用户对象.我不知道这是否正确.

我需要的是,当用户登录时,它在用户对象中设置一些值,以便所有其他页面都可以获得该值.

无论何时发生任何路由更改,控制器都应检查用户是否已登录.如果没有,它应该重新路由到登录页面.此外,如果用户已登录并返回页面,则应转到主页.控制器还应检查所有路由上的凭据.

我听说过ng-cookies,但我不知道如何使用它们.

我看到的许多例子都不是很清楚,他们使用某种访问角色或其他东西.我不希望这样.我只想要一个登录过滤器.有人可以给我一些想法吗?

gab*_*gab 179

我的解决方案分为3个部分:用户的状态存储在服务中,在路由更改时您观察的运行方法中,您检查是否允许用户访问所请求的页面,在主控制器中您查看是否用户状态发生变化.

app.run(['$rootScope', '$location', 'Auth', function ($rootScope, $location, Auth) {
    $rootScope.$on('$routeChangeStart', function (event) {

        if (!Auth.isLoggedIn()) {
            console.log('DENY');
            event.preventDefault();
            $location.path('/login');
        }
        else {
            console.log('ALLOW');
            $location.path('/home');
        }
    });
}]);
Run Code Online (Sandbox Code Playgroud)

您应该创建一个服务(我将其命名Auth),它将处理用户对象并有一个方法来知道用户是否被记录.

服务:

 .factory('Auth', function(){
var user;

return{
    setUser : function(aUser){
        user = aUser;
    },
    isLoggedIn : function(){
        return(user)? user : false;
    }
  }
})
Run Code Online (Sandbox Code Playgroud)

从你的app.run,你应该听取这个$routeChangeStart事件.当路由改变时,它将检查用户是否被记录(该isLoggedIn方法应该处理它).如果用户未登录,它将不会加载请求的路由,并且会将用户重定向到正确的页面(在您的情况下是登录).

loginController应该在你的登录页面被用来处理登录.它应该只是与Auth服务相互作用并将用户设置为已记录或未记录.

loginController:

.controller('loginCtrl', [ '$scope', 'Auth', function ($scope, Auth) {
  //submit
  $scope.login = function () {
    // Ask to the server, do your job and THEN set the user

    Auth.setUser(user); //Update the state of the user in the app
  };
}])
Run Code Online (Sandbox Code Playgroud)

从主控制器,您可以监听用户状态是否发生变化并对重定向做出反应.

.controller('mainCtrl', ['$scope', 'Auth', '$location', function ($scope, Auth, $location) {

  $scope.$watch(Auth.isLoggedIn, function (value, oldValue) {

    if(!value && oldValue) {
      console.log("Disconnect");
      $location.path('/login');
    }

    if(value) {
      console.log("Connect");
      //Do something when the user is connected
    }

  }, true);
Run Code Online (Sandbox Code Playgroud)

  • 如果用户点击F5并刷新怎么办?然后你的记忆Auth消失了. (34认同)
  • 以防其他人在运行此示例时遇到问题:在`routeChangeStart`回调中,您应检查该位置是否实际为"/ login"并允许:`if($ location.path()==="/ login" )返回;` (4认同)
  • 像魅力一样工作!而不是我使用的[Auth0 with AngularJS]提供的服务(https://github.com/auth0/auth0-angular). (2认同)

sp0*_*00m 109

这是另一种可能的解决方案,使用或的resolve属性.示例:$stateProvider$routeProvider$stateProvider

.config(["$stateProvider", function ($stateProvider) {

  $stateProvider

  .state("forbidden", {
    /* ... */
  })

  .state("signIn", {
    /* ... */
    resolve: {
      access: ["Access", function (Access) { return Access.isAnonymous(); }],
    }
  })

  .state("home", {
    /* ... */
    resolve: {
      access: ["Access", function (Access) { return Access.isAuthenticated(); }],
    }
  })

  .state("admin", {
    /* ... */
    resolve: {
      access: ["Access", function (Access) { return Access.hasRole("ROLE_ADMIN"); }],
    }
  });

}])
Run Code Online (Sandbox Code Playgroud)

Access 根据当前用户权限解析或拒绝承诺:

.factory("Access", ["$q", "UserProfile", function ($q, UserProfile) {

  var Access = {

    OK: 200,

    // "we don't know who you are, so we can't say if you're authorized to access
    // this resource or not yet, please sign in first"
    UNAUTHORIZED: 401,

    // "we know who you are, and your profile does not allow you to access this resource"
    FORBIDDEN: 403,

    hasRole: function (role) {
      return UserProfile.then(function (userProfile) {
        if (userProfile.$hasRole(role)) {
          return Access.OK;
        } else if (userProfile.$isAnonymous()) {
          return $q.reject(Access.UNAUTHORIZED);
        } else {
          return $q.reject(Access.FORBIDDEN);
        }
      });
    },

    hasAnyRole: function (roles) {
      return UserProfile.then(function (userProfile) {
        if (userProfile.$hasAnyRole(roles)) {
          return Access.OK;
        } else if (userProfile.$isAnonymous()) {
          return $q.reject(Access.UNAUTHORIZED);
        } else {
          return $q.reject(Access.FORBIDDEN);
        }
      });
    },

    isAnonymous: function () {
      return UserProfile.then(function (userProfile) {
        if (userProfile.$isAnonymous()) {
          return Access.OK;
        } else {
          return $q.reject(Access.FORBIDDEN);
        }
      });
    },

    isAuthenticated: function () {
      return UserProfile.then(function (userProfile) {
        if (userProfile.$isAuthenticated()) {
          return Access.OK;
        } else {
          return $q.reject(Access.UNAUTHORIZED);
        }
      });
    }

  };

  return Access;

}])
Run Code Online (Sandbox Code Playgroud)

UserProfile将当前用户的属性,并执行$hasRole,$hasAnyRole,$isAnonymous$isAuthenticated方法的逻辑(加上一个$refresh方法,稍后解释):

.factory("UserProfile", ["Auth", function (Auth) {

  var userProfile = {};

  var clearUserProfile = function () {
    for (var prop in userProfile) {
      if (userProfile.hasOwnProperty(prop)) {
        delete userProfile[prop];
      }
    }
  };

  var fetchUserProfile = function () {
    return Auth.getProfile().then(function (response) {
      clearUserProfile();
      return angular.extend(userProfile, response.data, {

        $refresh: fetchUserProfile,

        $hasRole: function (role) {
          return userProfile.roles.indexOf(role) >= 0;
        },

        $hasAnyRole: function (roles) {
          return !!userProfile.roles.filter(function (role) {
            return roles.indexOf(role) >= 0;
          }).length;
        },

        $isAnonymous: function () {
          return userProfile.anonymous;
        },

        $isAuthenticated: function () {
          return !userProfile.anonymous;
        }

      });
    });
  };

  return fetchUserProfile();

}])
Run Code Online (Sandbox Code Playgroud)

Auth 负责请求服务器,知道用户配置文件(例如链接到附加到请求的访问令牌):

.service("Auth", ["$http", function ($http) {

  this.getProfile = function () {
    return $http.get("api/auth");
  };

}])
Run Code Online (Sandbox Code Playgroud)

在请求时,服务器应返回此类JSON对象GET api/auth:

{
  "name": "John Doe", // plus any other user information
  "roles": ["ROLE_ADMIN", "ROLE_USER"], // or any other role (or no role at all, i.e. an empty array)
  "anonymous": false // or true
}
Run Code Online (Sandbox Code Playgroud)

最后,当Access拒绝承诺时,如果使用ui.router,$stateChangeError则会触发该事件:

.run(["$rootScope", "Access", "$state", "$log", function ($rootScope, Access, $state, $log) {

  $rootScope.$on("$stateChangeError", function (event, toState, toParams, fromState, fromParams, error) {
    switch (error) {

    case Access.UNAUTHORIZED:
      $state.go("signIn");
      break;

    case Access.FORBIDDEN:
      $state.go("forbidden");
      break;

    default:
      $log.warn("$stateChangeError event catched");
      break;

    }
  });

}])
Run Code Online (Sandbox Code Playgroud)

如果使用ngRoute,$routeChangeError将触发该事件:

.run(["$rootScope", "Access", "$location", "$log", function ($rootScope, Access, $location, $log) {

  $rootScope.$on("$routeChangeError", function (event, current, previous, rejection) {
    switch (rejection) {

    case Access.UNAUTHORIZED:
      $location.path("/signin");
      break;

    case Access.FORBIDDEN:
      $location.path("/forbidden");
      break;

    default:
      $log.warn("$stateChangeError event catched");
      break;

    }
  });

}])
Run Code Online (Sandbox Code Playgroud)

也可以在控制器中访问用户配置文件:

.state("home", {
  /* ... */
  controller: "HomeController",
  resolve: {
    userProfile: "UserProfile"
  }
})
Run Code Online (Sandbox Code Playgroud)

UserProfile然后包含服务器在请求时返回的属性GET api/auth:

.controller("HomeController", ["$scope", "userProfile", function ($scope, userProfile) {

  $scope.title = "Hello " + userProfile.name; // "Hello John Doe" in the example

}])
Run Code Online (Sandbox Code Playgroud)

UserProfile需要在用户登录或注销时刷新,以便Access可以使用新的用户配置文件处理路由.您可以重新加载整个页面,也可以调用UserProfile.$refresh().登录时的示例:

.service("Auth", ["$http", function ($http) {

  /* ... */

  this.signIn = function (credentials) {
    return $http.post("api/auth", credentials).then(function (response) {
      // authentication succeeded, store the response access token somewhere (if any)
    });
  };

}])
Run Code Online (Sandbox Code Playgroud)
.state("signIn", {
  /* ... */
  controller: "SignInController",
  resolve: {
    /* ... */
    userProfile: "UserProfile"
  }
})
Run Code Online (Sandbox Code Playgroud)
.controller("SignInController", ["$scope", "$state", "Auth", "userProfile", function ($scope, $state, Auth, userProfile) {

  $scope.signIn = function () {
    Auth.signIn($scope.credentials).then(function () {
      // user successfully authenticated, refresh UserProfile
      return userProfile.$refresh();
    }).then(function () {
      // UserProfile is refreshed, redirect user somewhere
      $state.go("home");
    });
  };

}])
Run Code Online (Sandbox Code Playgroud)

  • 我认为这是最简单也是最可扩展的答案 (3认同)
  • @LeblancMeneses谢谢:)只是为了让它更清楚:UNAUTHORIZED意味着*"我们不知道你是谁,所以我们不能说你是否有权访问这个资源,请先登录"*,而FORBIDDEN表示*"我们知道您是谁,而您的个人资料不允许您访问此资源"*. (2认同)

Dot*_*Bot 21

为各个路由定义自定义行为的最简单方法相当简单:

1)routes.js:requireAuth为任何所需的路线创建一个新属性(如)

angular.module('yourApp').config(function($routeProvider) {
    $routeProvider
        .when('/home', {
            templateUrl: 'templates/home.html',
            requireAuth: true // our custom property
        })
        .when('/login', {
            templateUrl: 'templates/login.html',
        })
        .otherwise({
            redirectTo: '/home'
        });
})
Run Code Online (Sandbox Code Playgroud)

2)在未绑定到内部元素的顶层控制器中ng-view(为避免与角度冲突$routeProvider),检查是否newUrl具有requireAuth属性并相应地采取相应措施

 angular.module('YourApp').controller('YourController', function ($scope, $location, session) {

     // intercept the route change event
     $scope.$on('$routeChangeStart', function (angularEvent, newUrl) {

         // check if the custom property exist
         if (newUrl.requireAuth && !session.user) {

             // user isn’t authenticated
             $location.path("/login");
         }
     });
 });
Run Code Online (Sandbox Code Playgroud)


Jas*_*son 6

几个月前我写了一篇关于如何使用Angular设置用户注册和登录功能的帖子,你可以在http://jasonwatmore.com/post/2015/03/10/AngularJS-User-Registration-和-Login-Example.aspx

我检查用户是否登录了该$locationChangeStart事件,这是我的主要app.js显示:

(function () {
    'use strict';
 
    angular
        .module('app', ['ngRoute', 'ngCookies'])
        .config(config)
        .run(run);
 
    config.$inject = ['$routeProvider', '$locationProvider'];
    function config($routeProvider, $locationProvider) {
        $routeProvider
            .when('/', {
                controller: 'HomeController',
                templateUrl: 'home/home.view.html',
                controllerAs: 'vm'
            })
 
            .when('/login', {
                controller: 'LoginController',
                templateUrl: 'login/login.view.html',
                controllerAs: 'vm'
            })
 
            .when('/register', {
                controller: 'RegisterController',
                templateUrl: 'register/register.view.html',
                controllerAs: 'vm'
            })
 
            .otherwise({ redirectTo: '/login' });
    }
 
    run.$inject = ['$rootScope', '$location', '$cookieStore', '$http'];
    function run($rootScope, $location, $cookieStore, $http) {
        // keep user logged in after page refresh
        $rootScope.globals = $cookieStore.get('globals') || {};
        if ($rootScope.globals.currentUser) {
            $http.defaults.headers.common['Authorization'] = 'Basic ' + $rootScope.globals.currentUser.authdata; // jshint ignore:line
        }
 
        $rootScope.$on('$locationChangeStart', function (event, next, current) {
            // redirect to login page if not logged in and trying to access a restricted page
            var restrictedPage = $.inArray($location.path(), ['/login', '/register']) === -1;
            var loggedIn = $rootScope.globals.currentUser;
            if (restrictedPage && !loggedIn) {
                $location.path('/login');
            }
        });
    }
 
})();
Run Code Online (Sandbox Code Playgroud)