Как обрабатывать авторизацию на основе ролей в AngularJS?

Я создаю веб-приложение, которое удовлетворит два требования для пользователей. Примечание: я новичок в AngularJS в качестве платформы для веб-разработки.

Front-end - 1: это функция поиска, где пользователи могут искать определенные документы и исследования на основе поиска по ключевым словам и фильтров. Это было реализовано с использованием MySQL для получения данных и отображения с помощью AngularJS.

Внешний интерфейс - 2: у пользователей будет возможность создать учетную запись в веб-приложении. Цели учетной записи:

  1. Сохраните свои поисковые запросы.
  2. Если администратор связывает каждого пользователя с определенной ролью, то эти пользователи получат доступ к дополнительным параметрам, таким как изменение документов, присутствующих в базе данных, а также загрузка новых документов и хоста других страниц.

Мой вопрос:

Как обрабатывать авторизацию на основе ролей в AngularJS? Я не могу понять, как создать структуру, которая включает в себя следующие функции: - Пользователи получают связанную с ними роль - Запрещает пользователям доступ к страницам или функциям, которые не связаны с этими ролями

Я прочитал несколько статей по SO, а также учебные пособия, но каждый учебник заканчивается тем, что автор говорит, что авторизация на основе ролей должна обрабатываться на стороне сервера, и я понимаю, почему это правда.

Было бы здорово, если бы кто-нибудь мог указать мне на учебные пособия или рецензии, в которых авторизация на основе ролей реализована на стороне сервера для AngularJS.

Спасибо!


person CalmWinds    schedule 27.10.2015    source источник


Ответы (1)


Я использую авторизацию на основе ролей как на внутреннем, так и на внешнем интерфейсе. Поскольку для маршрутизации я использую UI-Router, лучшим ресурсом, который я нашел (и улучшил в соответствии с моими потребностями), является эта статья:

срок действия ссылки истек

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


Изменить: добавление кода.

Во-первых, вам нужно где-то хранить разрешения пользователя, например для объекта пользователя, сериализованного в localStorage:

{"id":1,"name":"user","created_at":"2016-04-17 18:58:19","gender":"m","roles":["admin"]}

Затем у вас есть две важные части:

  • директива - чтобы определить, должен ли элемент быть видимым или нет на основе назначенного разрешения
  • service - для обработки проверки авторизации

Директива:

(function() {
  'use strict';

  angular
    .module('app')
    .directive('access', access);

  /** @ngInject */
  function access(authorization) {
    var directive = {
      restrict: 'A',
      link: linkFunc,
    };

    return directive;

    /** @ngInject */
    function linkFunc($scope, $element, $attrs) {
      var makeVisible = function () {
        $element.removeClass('hidden');
      };

      var makeHidden = function () {
        $element.addClass('hidden');
      };

      var determineVisibility = function (resetFirst) {
        var result;

        if (resetFirst) {
          makeVisible();
        }

        result = authorization.authorize(true, roles, $attrs.accessPermissionType);

        if (result === authorization.constants.authorised) {
          makeVisible();
        } else {
          makeHidden();
        }
      };

      var roles = $attrs.access.split(',');

      if (roles.length > 0) {
          determineVisibility(true);
      }
    }
  }

})();

Вам необходимо настроить CSS так, чтобы элементы с классом hidden не были видны.

Сервис:

(function() {
  'use strict';

  angular
    .module('app')
    .factory('authorization', authorization);

  /** @ngInject */
  function authorization($rootScope) {
    var service = {
      authorize: authorize,
      constants: {
        authorised: 0,
        loginRequired: 1,
        notAuthorised: 2
      }
    };

    return service;

    function authorize(loginRequired, requiredPermissions, permissionCheckType) {
      var result = service.constants.authorised,
          user = $rootScope.currentUser,
          loweredPermissions = [],
          hasPermission = true,
          permission;

      permissionCheckType = permissionCheckType || 'atLeastOne';

      if (loginRequired === true && user === undefined) {
          result = service.constants.loginRequired;

      } else if ((loginRequired === true && user !== undefined) &&
                  (requiredPermissions === undefined || requiredPermissions.length === 0)) {
          result = service.constants.authorised;

      } else if (requiredPermissions) {

          loweredPermissions = [];

          angular.forEach(user.roles, function (permission) {
              loweredPermissions.push(permission.toLowerCase());
          });

          for (var i = 0; i < requiredPermissions.length; i += 1) {
              permission = requiredPermissions[i].toLowerCase();

              if (permissionCheckType === 'combinationRequired') {
                  hasPermission = hasPermission && loweredPermissions.indexOf(permission) > -1;
                  // if all the permissions are required and hasPermission is false there is no point carrying on
                  if (hasPermission === false) {
                      break;
                  }
              } else if (permissionCheckType === 'atLeastOne') {
                  hasPermission = loweredPermissions.indexOf(permission) > -1;
                  // if we only need one of the permissions and we have it there is no point carrying on
                  if (hasPermission) {
                      break;
                  }
              }
          }

          result = hasPermission ?
                   service.constants.authorised :
                   service.constants.notAuthorised;
      }

      return result;
    }
  }
})();

Теперь вы можете использовать директиву для отображения / скрытия элемента:

<a ui-sref="app.administration" class="btn btn-primary pull-right" access="admin">Administration</a>

Конечно, это скроет только элемент в DOM, поэтому вы также должны выполнить проверку авторизации на сервере.

Эта первая часть решена для отображения / скрытия элементов в пользовательском интерфейсе, но вы также можете защитить маршруты приложений.

Определение маршрута:

(function() {
  'use strict';

  angular
    .module('app')
    .config(routeConfig);

  /** @ngInject */
  function routeConfig($stateProvider) {
    $stateProvider
      .state('app.dashboard', {
        url: '/dashboard',
        data: {
          access: {
            loginRequired: true
          }
        },
        templateUrl: 'template_path',
        controller: 'DashboardController as vm'
      }
  }
})();

а теперь просто проверьте разрешение в событии $stateChangeStart

(function() {
  'use strict';

  angular
    .module('app')
    .run(runBlock);

  /** @ngInject */
  function runBlock($rootScope, $state, authorization) {
    $rootScope.$on('$stateChangeStart', function(event, toState) {
      // route authorization check
      if (toState.data !== undefined && toState.data.access !== undefined) {
        authorised = authorization.authorize(toState.data.access.loginRequired,
                                             toState.data.access.requiredPermissions,
                                             toState.data.access.permissionCheckType);

        if (authorised === authorization.constants.loginRequired) {
          event.preventDefault();
          $state.go('app.login');
        } else if (authorised === authorization.constants.notAuthorised) {
          event.preventDefault();
          $state.go('app.dashboard');
        }
      }
    });
  }

})();
person Adrian    schedule 27.10.2015
comment
Спасибо @ sh-ado-w, я бы реализовал то решение, о котором вы упомянули выше. Я дам вам знать, сработает это или нет. - person CalmWinds; 03.11.2015
comment
Ссылка больше не работает. Лучше размещать контент в SO, чем размещать ссылки за его пределами. - person eabates; 15.04.2016
comment
Я добавил код из своего приложения, который, как мне кажется, был основан на этой статье. - person Adrian; 17.04.2016