Website development
JavaScript
Angular
4 December 2014

AngularJS + UI Router: проверка авторизации и прав доступа

Tutorial
Если ваше приложение предполагает авторизацию пользователей и/или проверку прав доступа, то вам придется либо изобретать велосипед, либо гуглить в поисках подходящего решения. В принципе, я тоже это делал. В итоге я принял приемлемым для себя описанный ниже вариант.

Предпосылки


Информацию об авторизованном пользователе я решил хранить в sessionStorage, копируя её при запуске приложения в $rootScope. Также по рекомендации авторов UI Router я храню в $rootScope значения объекты $state и $stateParam, для удобного доступа. Информацию же о доступе к тому или иному состоянию можно передавать через блок data при описании самого состояния. Поскольку в моем приложении везде закрыт доступ, я решил идти от обратного и добавлять значение noLogin = true для состояний, которые не требуют авторизации, например страницы ввода логина, восстановления пароля или регистрации.

angular.module('myApp.auth', [
    'ui.router'
  ])
  .config(['$stateProvider', '$urlRouterProvider',
    function ($stateProvider, $urlRouterProvider) {

      $stateProvider
        .state('auth', {
          url: '/auth',
          abstract: true,
          template: '<ui-view>'
        })

      .state('auth.login', {
        url: '/login',
        templateUrl: 'src/auth/partials/login.html', 
        data: {
          'noLogin': true
        }
      });
  ]);


Создание сервиса для пре-роутинга


Проверять авторизацию и права доступа нужно в самом начале, до работы роутера, перед тем, как он отправит посетителя на запрошенное состояние (предполагается, что вы знаете, что UI Router управляет не положениями, а состояниями. Больше читайте в официальной документации). Хороший способ это сделать — повесить слушателя на событие $stateChangeStart в методе run() вашего главного модуля. Чтобы не захламлять функционалом тело метода, который может быть объемным и сложным, я вынес его в отдельный сервис, в методе run() я просто вызываю метод сервиса. Думаю, дальше объяснения не понадобятся.

angular.module('myApp.auth')
  .service('SessionService', [
    '$injector',
    function($injector) {
      "use strict";

      this.checkAccess = function(event, toState, toParams, fromState, fromParams) {
        var $scope = $injector.get('$rootScope'),
            $sessionStorage = $injector.get('$sessionStorage');

        if (toState.data !== undefined) {
          if (toState.data.noLogin !== undefined && toState.data.noLogin) {
            // если нужно, выполняйте здесь какие-то действия 
            // перед входом без авторизации
          }
        } else {
          // вход с авторизацией
          if ($sessionStorage.user) {
            $scope.$root.user = $sessionStorage.user;
          } else {
            // если пользователь не авторизован - отправляем на страницу авторизации
            event.preventDefault();
            $scope.$state.go('auth.login');
          }
        }
      };
    }
  ]);


Собираем все вместе


Ну и остается последний штрих, чтобы все это заработало — повесить слушателя на событие сервиса $state.

angular.module('myApp', [
  'myApp.auth',
  'ui.router',
  'ngStorage'
])

.run([
  '$rootScope', '$state', '$stateParams', 'SessionService',
  function ($rootScope, $state, $stateParams, SessionService) {

    $rootScope.$state = $state;
    $rootScope.$stateParams = $stateParams;

    $rootScope.user = null;

    // Здесь мы будем проверять авторизацию
    $rootScope.$on('$stateChangeStart',
      function (event, toState, toParams, fromState, fromParams) {
        SessionService.checkAccess(event, toState, toParams, fromState, fromParams);
      }
    );
  }
])


Заключение


Думаю, данного примера вполне достаточно, чтобы использовать его как идею и написать собственную авторизацию и проверку прав доступа с какими угодно плюшками. Например, в тот же блок data при описании состояния вы можете передавать какие-либо RBAC-правила, а затем проверять их в своем сервисе.
Что вы используете для управления маршрутами в Angular JS?
39.8% ngRoute 296
62.1% UI Router 462
1.8% Я знаю решение получше (напишите в комментарии) 14
2% Я сам написал роутер (поделитесь) 15
743 users voted. 251 user abstained.

+23
71.7k 272
Comments 23
Top of the day