Skip to content

Посты

Защита AngularJS с помощью Claims

14 февраля 2015 г. • 5 мин чтения

Защита AngularJS с помощью Claims

В какой-то момент приложению требуется авторизация. Это означает, что разные уровни доступа ведут себя по-разному на веб-сайте (или в чем угодно еще). Это может быть что угодно - от просмотра данных до целых областей, которые недоступны группе пользователей.

В приложениях, не являющихся одностраничными (SPA), утверждение или роль связаны с данными или областью приложения - либо у пользователя есть эта роль или утверждение, либо нет. В SPA все то же самое, но с огромной оговоркой. SPA загружается в браузер. В этот момент браузер получает полный контроль над кодом. Злоумышленник может изменить код для своих целей.

Поскольку SPA не могут быть защищены, аутентификация и авторизация в SPA - это просто пользовательский опыт. Вся значимая безопасность должна обеспечиваться на веб-сервере. Эта статья не охватывает защиту вашего API от атак. Я рекомендую посмотреть видео от Pluralsight или прочитать статью, посвященную безопасности для вашей серверной технологии.

Цель этой статьи - показать вам, как я добавил пользовательский опыт авторизации в мое Angular 1.x SPA.

Области безопасности

Я выделил 3 области пользовательского интерфейса, которые нуждаются в авторизации: Элементы (HTML), Маршруты и Данные.

Напоминаю, что защита SPA не является заменой защиты сервера. Разрешения на клиенте нужны просто для того, чтобы честные люди оставались честными и чтобы обеспечить пользователю хороший опыт.

3 области подробно:

Элементы

Вам нужно будет скрывать определенные HTML-элементы. Это может быть метка, таблица с данными, кнопка или любой элемент на странице.

Маршруты

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

Данные

Иногда скрытия элементов в представлении недостаточно. Сообразительный пользователь может просто посмотреть исходный код и увидеть скрытые данные в HTML-источнике или наблюдать их поток в браузер. Мы хотим, чтобы данные вообще не извлекались.

Добавление безопасности - дело хитрое. Сначала я попытался ограничить доступ на уровне HTTP API (на клиенте). Я быстро понял, что это не сработает. У пользователя может не быть прямого доступа к данным, но это не означает, что у него нет косвенного доступа к данным. На уровне HTTP API (обычно одном из самых низких в приложении) мы не можем определить контекст вызова и поэтому не можем применить к нему вопросы безопасности.

Ниже я привел примеры кода:

Код

Я создал сервис для кода проверки авторизации. Это сердце авторизации. Все запросы авторизации используют этот сервис для проверки того, авторизован ли пользователь для конкретного действия.

angular.module('services')
    .service('AuthorizationContext',function(_, Session){

        this.authorizedExecution = function(key, action){

            //Looking for the claim key that was passed in. If it exists in the claim set, then execute the action.
            Session.claims(function(claims){
                var claim = findKey(key, claims);

                //If Claim was found then execute the call.
                //If it was not found, do nothing
                if(claim !== undefined){
                    action();
                }
            });
        };

        this.authorized = function(key, callback){
            //Looking for the claim key that was passed in. If it exists in the claim set, then execute the action.
            Session.claims(function(claims){
                var claim = findKey(key, claims);

                //If they don't have any security key, then move forward and authorization.
                var valid = claim !== undefined;
                callback(valid);
            });
        };

        //this.agencyViewKey = '401D91E7-6EA0-46B4-9A10-530E3483CE15';

        function findKey(key, claims){
            var claim = _.find(claims, function(item){
                return item.value === key;
            });

            return claim;
        }
    });

Директива авторизации

Директива авторизации может быть применена к любому HTML-элементу, который вы хотите скрыть от пользователей без определенного уровня доступа. Если у пользователя есть токен доступа как часть их утверждений, им разрешено видеть элемент. Если у них его нет, он скрывается от них.

angular.module('directives')
    .directive('authorize', ['$compile', 'AuthorizationContext', function($compile, AuthorizationContext) {
        return {
            restrict: 'A',
            replace: true,
            //can't have isolated the scope in a shared directive
            link:function ($scope, element, attributes) {

                var securityKey = attributes.authorize;
                AuthorizationContext.authorized(securityKey, function(authorized){
                    var el = angular.element(element);
                    el.attr('ng-show', authorized);

                    //remove the attribute, otherwise it creates an infinite loop.
                    el.removeAttr('authorize');
                    $compile(el)($scope);
                });
            }
        };
    }]);

Элементы

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

<tabset>
<tab ng-cloak heading="Users" authorize="{{allowUserManagement}}">
...html content
</tab>
</tabset>

Маршруты

Я использую ui-router. К сожалению, для тех, кто его не использует, у меня нет кода для стандартного AngularJS роутера.

В $stateChangeStart я аутентифицирую маршрут. Вот код в этом событии.

$rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams){
   AuthenticationManager.authenticate(event, toState, toParams);
});

Функция, которая авторизует маршрут. Если он авторизован, маршруту разрешается продолжить. Если он не авторизован, пользователю отображается сообщение, и он перенаправляется на домашнюю страницу.

function authorizedRoute(toState, location, toaster, breadCrumbs){
   if(toState.authorization !== undefined){
       AuthorizationContext.authorized(toState.authorization, function(authorized){
           if(!authorized){
               toaster.pop('error', 'Error', 'You are not authorized to view this page.');
               location.path("/search");
           } else {
               breadCrumbs();
           }
       });
   } else{
       breadCrumbs();
   }
}

В этом определении роутера вы заметите свойство под названием ‘authorization’. Если у пользователя есть это утверждение, ему разрешается продолжить.

angular.module('agency',
    [
        'ui.router',
        'services'
    ])
    .config(function config($stateProvider){
    $stateProvider.state( 'agency', {
        url: '/agency',
        controller: 'agency.index',
        templateUrl: 'agency/agency.tpl.html',
        authenticate: true,
        authorization:'401d91e7-6ea0-46b4-9a10-530e3483ce15',
        data:{ pageTitle: 'Agency' }
    });
});

Данные

В некоторых случаях вы не хотите делать запрос к серверу за данными. Если у пользователя есть утверждение, ему будет разрешено сделать запрос.

Вышеупомянутый AuthorizationContext в начале статьи показывает код для authoriedExecution. Здесь вы видите его использование.

AuthorizationContext.authorizedExecution(Keys.authorization.allowUserManagement, function(){
    //execute code, if the loggedin user has rights.

                });

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

Автор: Чак Конвей специализируется на разработке программного обеспечения и генеративном ИИ. Свяжитесь с ним в социальных сетях: X (@chuckconway) или посетите его на YouTube.

↑ Наверх

Вам также может понравиться