Skip to content

Посты

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

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

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

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

В традиционных приложениях (не SPA) claim или роль связаны с данными или областью приложения — либо у пользователя есть эта роль или claim, либо нет. В 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;
        }
    });

Директива Authorize

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

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);
                });
            }
        };
    }]);

Элементы

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

<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’. Если у пользователя есть этот claim, ему разрешено продолжить.

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' }
    });
});

Данные

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

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

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

                });

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

Автор: Chuck Conway — инженер AI с почти 30-летним опытом разработки программного обеспечения. Он создает практические системы AI — конвейеры контента, агенты инфраструктуры и инструменты, которые решают реальные проблемы — и делится тем, что он узнает на этом пути. Свяжитесь с ним в социальных сетях: X (@chuckconway) или посетите его на YouTube и на SubStack.

↑ Вернуться в начало

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