Angular路由异步授权

Angular route async authorization

Angular 不提供任何 authorization/access 路由权限(我说的是默认 Angular 路由 1.x 而不是 beta 2.0 或 UI路线)。但我确实必须实施它。

我遇到的问题是我有一个调用服务器来提供此信息和 returns 承诺的服务。但是这个数据只是获取一次然后缓存在客户端,但是还是需要获取一次。

我现在想处理 $routeChangeStart 事件,检查下一条路线是否定义了特定的 属性 authorize: someRole。然后,此处理程序应使用我之前提到的服务获取该数据,并对返回的数据采取相应的行动。

除了将 resolves 添加到我的所有路线之外,还有什么想法吗?我能以某种方式集中执行此操作吗?所有适用的路线一次?

最终解决方案

在接受的答案的帮助下,我能够实现一个相当简单且集中的解决方案来执行异步授权。 Click here 查看它的运行情况或检查其内部工作代码。

ui-router 有很多您可以轻松操作的事件。我一直在用它。

State Change Events 应有尽有。像这样的东西将在 AngularJS 2.x.

中实现

但是,如果您正在寻找本机 Angular 1.x.y 路由器的解决方案,则此解决方案对您没有帮助。抱歉

如果你可以使用 ui-router,你可以这样做:

.state('root', {
      url: '',
      abstract: true,
      templateUrl: 'some-template.html',
      resolve: {
        user: ['Auth', function (Auth) {
          return Auth.resolveUser();
        }]
      }
    })

Auth.resolveUser() 只是加载当前用户的后端调用。它 returns 一个承诺,因此路由将在更改之前等待它加载。

路由是抽象的,因此其他控制器必须从它继承才能工作,而且您还有一个额外的好处,即所有子控制器都可以通过解析访问当前用户。

现在您在 app.run() 中捕获了 $stateChangeStart 事件:

$rootScope.$on('$stateChangeStart', function (event, next) {
        if (!Auth.signedIn()) { //this operates on the already loaded user
          //user not authenticated
          // all controllers need authentication unless otherwise specified
          if (!next.data || !next.data.anonymous) {
            event.preventDefault();
            $state.go('account.login');
          }
        }else{
         //authenticated 
         // next.data.roles - this is a custom property on a route.
         if(!Auth.hasRequiredRole(next.data.roles)){
            event.preventDefault();
            $state.go('account.my'); //or whatever
         }             
        }
      });

那么需要ui扮演角色的路由可以是这样的:

.state('root.dashboard', {
         //it's a child of the *root* route
          url: '/dashboard',
          data: {
             roles: ['manager', 'admin']
          }
          ...
        });

希望它有意义。

最简单的方法是处理当前路由的 resolve 依赖关系,$routeChangeStart 是管理它的好地方。 Here's an example.

app.run(function ($rootScope, $location) {
  var unrestricted = ['', '/login'];

  $rootScope.$on('$routeChangeStart', function (e, to) {
    if (unrestricted.indexOf(to.originalPath) >= 0)
      return;

    to.resolve = to.resolve || {};
    // can be overridden by route definition
    to.resolve.auth = to.resolve.auth || 'authService';
  });

  $rootScope.$on('$routeChangeError', function (e, to, from, reason) {
    if (reason.noAuth) {
      // 'to' path and params should be passed to login as well
      $location.path('/login');
    }
  });
});

另一种选择是将 default 方法添加到 $routeProvider 并修补 $routeProvider.when 以从默认对象扩展路由定义。

这个问题我已经处理过很多次了,我也开发了一个模块(github)。 我的模块(建立在 ui.router 之上)基于 $stateChangeStart(ui.router 事件),但概念与默认的 ng.route 模块相同,只是实现方式不同。

总而言之,我认为处理路由更改事件并不是执行身份验证检查的好方法: 例如,当我们需要通过 ajax 获取 acl 时,事件就帮不了我们了。

我认为一个好方法是自动为每个 "protected" 状态附加一个解析...

不幸的是,ui.Router 没有提供 API 来拦截状态创建,所以我在 $stateProvider.state 方法之上使用一些变通方法开始了我的模块返工。

毫无疑问,我正在寻找不同的意见,以便找到在 AngularJS 中实现身份验证服务的正确方法。

如果有人对这项研究感兴趣...请在我的 github 上开一个问题并讨论