Nano Hash - криптовалюты, майнинг, программирование

AngularJS, ui.router, загрузка шаблона и контроллера в зависимости от роли пользователя

Я разработал одностраничное приложение, использующее REST API. Пользователи должны войти в систему, чтобы получить доступ к приложению. Когда пользователь входит в систему, он перенаправляется на /dashboard. На этом URL-адресе/маршруте я хотел бы загрузить другой шаблон и контроллер в зависимости от роли пользователя (например, обычный пользователь user или admin).

Я просмотрел https://github.com/angular-ui/ui-router/wiki в разделе раздел шаблонов, но ни один из вариантов не поддерживает то, чего я пытаюсь достичь.

  • Используя templateUrl и функцию (stateParams), я не могу внедрить службу, которая помогает мне определить роль пользователя, чтобы я мог загрузить шаблон, например views/пользователь/dashboard.html или views/admin/dashboard.html
  • Используя templateProvider, я должен внедрить службу, которая поможет мне определить роль пользователя, но как мне загрузить шаблон?

Любое решение также должно загружать разные контроллеры в зависимости от роли пользователя, например UserDashboardController или AdminDashboardController.

По сути, мне нужен один маршрут, который загружает другой шаблон И контроллер на основе переменной роли пользователя, которая устанавливается в службе при входе пользователя в систему.

Правильно ли я думаю, или я должен реализовать другое решение?

Любая помощь по этому вопросу будет принята с благодарностью.


  • Вы нашли какое-нибудь решение? 31.08.2014
  • @MyTitle, ваша цель просто функционально разделить инструменты пользователя и администратора? Вы беспокоитесь о безопасности, функциональности, обоих? Вы хотите, чтобы экраны администратора представляли собой супернабор пользовательских экранов (со ссылками администратора и такими инструментами, как редактирование, удаление, создание), или вы хотите создать совершенно разные пользовательские интерфейсы? 04.09.2014
  • @DaveA да, первый вариант: «ищу, чтобы экраны администратора представляли собой набор пользовательских экранов (с ссылками администратора и такими инструментами, как редактирование, удаление, создание)». т.е. нет большой разницы между обычными экранами пользователя и администратора 05.09.2014
  • @MyTitle: вы можете попробовать первое решение в моем ответе. Та же идея и с переключением функций на странице. В этом случае вы не настраиваете права, предполагается, что права жестко закодированы в каждой роли (вы можете расширить это в будущем, чтобы сделать права настраиваемыми или добавить больше ролей). 07.09.2014

Ответы:


1

Загрузка шаблона и контроллера в зависимости от роли пользователя

Хотя технически функция ui-router templateUrl не поддерживает внедрение сервисов, вы можете использовать templateProvider для внедрения service, содержащего переменную role, или загрузить ее асинхронно, а затем использовать $templateFactory для возврата содержимого HTML. Рассмотрим следующий пример:

var app = angular.module('app', ['ui.router']);

app.service('session', function($timeout, $q){
    this.role = null;

    this.loadRole = function(){
        //load role using axax request and return promise
    };
});

app.config(function($stateProvider, $urlRouterProvider){
    $stateProvider.state('dashboard', {
        url: '/dashboard',
        templateProvider: function(session, $stateParams, $templateFactory){
          return session.loadRole().then(function(role){
              if(session.role == 'admin'){
                return $templateFactory.fromUrl('/admin/dashboard.html', $stateParams);
              } else {
                return $templateFactory.fromUrl('/user/dashboard.html', $stateParams);
              }
          });
        }
      });

    $urlRouterProvider.otherwise('/dashboard');
});

Что касается controller, вы можете указать, что хотите использовать определенный контроллер внутри корневого элемента каждого шаблона с ng-controller. Или аналогичным образом вы можете использовать параметр controllerProvider для внедрения service, который уже будет разрешен role с помощью templateProvider. Взгляните на следующий пример опции controllerProvider внутри определения состояния ui-router:

controllerProvider: function(session){
  if(session.role == 'admin'){
    return 'AdminCtrl';
  } else {
    return 'UserCtrl';  
  }
}

Конечно, вы можете легко удалить дубликаты из этого кода и определить более доступный микро-DSL, чтобы упростить определение различных правил для определенных ролей и представлений.

Следующая демонстрация должна помочь вам понять код.

Это правильный подход?

Как обычно, это сильно зависит от контекста. Чтобы помочь вам найти ответ, позвольте мне сначала предложить следующие вопросы:

  • Насколько различаются представления для ролей?

Собираетесь ли вы скрыть только пару button и других элементов действия, в основном делая страницу доступной только для чтения для обычных пользователей и редактируемой для суперпользователей? Если изменения будут небольшими, я бы, вероятно, использовал те же представления и скрывал только определенные элементы, возможно, подделывая директиву, подобную ng-if, которая позволяла бы декларативно включать/отключать определенные функции only-role='operator, admin'. С другой стороны, если представления будут сильно различаться, использование разных шаблонов может значительно упростить разметку.

  • Насколько действия, доступные на конкретной странице, различаются в зависимости от роли?

Отличаются ли действия, внешне похожие на разные роли, внутренней работой? Например, если у вас есть действие Редактировать, доступное как для роли user, так и для роли admin, но в одном случае оно запускает мастер как пользовательский интерфейс, а в другом — в сложной форме для опытных пользователей, то есть отдельный controller имеет больше смысла. С другой стороны, если admin действия представляют собой надмножество user действий, то использование одного контроллера кажется более простым. Обратите внимание, что в обоих случаях сохранение controller вещей окупается — они должны привязывать представления только к поведению, которое инкапсулировано в службах/моделях представлений/моделях/выборе имени

  • Будет ли у вас много контекстно отдельных ссылок, ведущих на определенную страницу из разных мест приложения?

Например, возможность обеспечить переход к определенной странице, просто написав ui-sref="dashboard" независимо от текущего пользователя role, может быть полезной, если он существует в различных контекстах. Если это так, то их определение в рамках одного маршрута/состояния кажется более удобным для сопровождения, чем условная логика, используемая для построения разных ui-sref/ng-href на основе роли. Однако вы также можете динамически определять маршруты/состояния в зависимости от роли пользователя — загружаться динамически или нет

  • Будут ли представления и действия, доступные для разных ролей на конкретной странице, развиваться отдельно или вместе?

Иногда мы сначала создаем функции для обычных пользователей, затем для премиальных, а затем для ультимативных. Нет ничего необычного в том, чтобы разделить работу над страницами для user и admin между членами команды, особенно если можно легко провести четкие границы. В таком случае, имея отдельные views и controllers, разработчики могут просто работать, избегая конфликтов. Конечно, это не все радуги и единороги - команда должна быть очень дисциплинированной, чтобы удалить дублирование, которое, скорее всего, произойдет.

Надеюсь, что мои советы помогут вам определиться.

31.08.2014
  • Спасибо. Но в вашем случае admin — жестко запрограммированная роль. Но я получу роль пользователя асинхронно, используя запрос AJAX. Так что я не уверен, что это сработает: role = injector.get('session').role, 01.09.2014
  • @MyTitle Я обновил свой ответ, чтобы поддерживать асинхронное разрешение role 01.09.2014
  • Ух ты! Отличный ответ! +100 08.10.2015

  • 2

    Правильно ли я думаю, или я должен реализовать другое решение?

    ИМО, вы не должны делать это таким образом.

    Здесь я предлагаю 2 других решения в зависимости от того, как реализовано ваше приложение.

    1) Если права ваших ролей могут быть настроены (у вас может быть отдельная страница для настройки ваших ролей, назначения прав вашим ролям,...). Затем используйте только 1 шаблон и 1 контроллер для своих ролей (обычные пользователи, пользователи-администраторы и т. д.) и используйте ng-show, ng-class,.. для соответствующего отображения вашего HTML.

    В этом случае нам все равно, является ли пользователь обычным пользователем или администратором, это просто имя нашей роли. Что нас волнует, так это права, и они динамические => Поэтому мы должны отображать html динамически на основе настроенных прав (конечно, на стороне сервера также есть проверки, когда пользователи выполняют действие для запретить пользователю создавать вредоносный HTTP-запрос и отправлять его на сервер). Если бы мы использовали для этого отдельные шаблоны, было бы бесчисленное количество случаев.

    Смысл этого решения в том, что функции страницы одинаковы с вашими ролями, вам просто нужно показать/скрыть функции страницы в зависимости от пользователя.

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

    Причина в том, что представление администратора и обычное представление пользователя имеют разные функции (которые должны быть отделены друг от друга).

    01.09.2014
  • Я думаю, что половина награды лучше, чем ничего. Должен был быть полным. Опять же, трудно удовлетворить кого-то, кто не знает, чего хочет. 08.09.2014

  • 3

    Если вы используете версию angular выше 1.2, вы можете сделать директиву с templateUrl в качестве функции.

    Итак, основная идея заключается в том, что у вас есть панель мониторинга, на которой есть пользовательская директива, определяющая шаблон на основе уровня пользователя. Что-то вроде этого:

    (function () {
      'use strict';
      angular.module('App.Directives')
        .directive('appDashboard', ['UserManager', function (UserManager) {
          return {
            restrict: 'EA',
            templateUrl: function(ele, attr){
                if (UserManager.currentUser.isAdmin){
                    return 'admin.html';
                }else{
                    return 'user.html';
                }
            }
          };
        }]);
    })(); 
    
    02.05.2014
  • это запускается только один раз и имеет проблемы, если пользователь выходит из системы и снова входит в систему с другой ролью. 07.09.2014

  • 4

    Я. не использовать "...один маршрут, который загружает другой шаблон...", было бы моим предложением, моим ответом.

    Если возможно:

    Попробуйте сделать шаг назад и пересмотреть весь дизайн и
    попытаться ослабить ощущение того, что наше приложение пользователей интересует url.

    Они не. И если они действительно понимают, что такое url, адресная строка... они используют ее для copy, send и paste... не для исследования ее частей...

    II. Предложение: Принудительно использовать UI-маршрутизатор состояния:

    ... UI-Router организовано вокруг состояний, которые могут НЕОБЯЗАТЕЛЬНО иметь маршруты, а также другое поведение, прикрепленное...

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

    III. Как мы можем извлечь выгоду из создания нашего приложения вокруг штатов?

    Разделение интересов - должно быть нашей целью.

    Состояние — это единица, которая собирает некоторые представления/контроллеры, преобразователи, пользовательские данные...

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

    • администрирование записи пользователя/сотрудника
    • список пользователей / сотрудников - информация аля PhoneList (только электронная почта, телефон...)
    • Администрирование безопасности - Каковы права пользователя ...

    И снова может быть много-много состояний. Наличие даже сотни состояний не будет проблемой производительности. Это всего лишь определения, набор ссылок на другие части, которые следует использовать... позже... если они действительно нужны.

    После того как мы определили варианты использования, пользовательские истории на уровне состояния, мы можем сгруппировать их в наборы/иерархии.
    Эти группы впоследствии могут быть представлены разным ролям пользователей в другом формате (разные пункты меню).

    Но в итоге мы получили много свободы и упростили ремонтопригодность.

    IV. Обеспечение бесперебойной работы приложения и развитие

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

    Разделение определений состояния (как единицы работы) и их иерархий (какая роль пользователя может получить доступ к каким состояниям) упростит управление.

    Применение безопасности за пределами состояний (слушатели событий аля '$stateChangeStart' ) намного проще, чем бесконечный рефакторинг поставщиков шаблонов. Кроме того, основная часть безопасности должна по-прежнему применяться на сервере, независимо от того, что позволяет пользовательский интерфейс.

    В. Резюме:

    Хотя есть такая замечательная функция, как templateProvider, которая могла бы сделать для нас кое-что интересное (например, здесь: Изменение меню навигации с помощью UI-Router в AngularJs)...

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

    31.08.2014
  • Благодарю. Звучит хорошо, но не могли бы вы привести какой-нибудь пример? 01.09.2014
  • Не уверен, что это предложение по дизайну может быть снабжено достаточно простым примером... но подумаю об этом позже сегодня... или позже. Основная часть моей точки зрения такова: определяйте состояния как можно проще. Их может быть много. Как только вы создадите навигацию для пользователей, сделайте ее зависимой от роли (больше настроек навигации для каждой роли). При необходимости введите некоторую проверку событий ... но реальная безопасность применяется на сервере (получайте данные, только если у пользователя есть требуемая роль). Итак, это скорее принцип дизайна/архитектуры, чем простой ответ на вопрос о прецедентах... Буду рад, если это хоть немного поможет... позже;) 01.09.2014
  • Я вижу проблему с подходом, предлагаемым этим ответом. Пользователь открывает ww.someapp.com/ и перенаправляется с помощью angular на #!/, что предполагает, что пользователь может войти или не войти в данный момент. Очевидно, что зарегистрированным пользователям не нужно видеть домашнюю страницу маркетинга, они предпочитают, чтобы их эффективно перенаправляли на панель инструментов при посещении /#!/ или / пути. 31.03.2015

  • 5

    Вам не нужно делать это с маршрутизатором.

    Самое простое — использовать один единый шаблон для всех ролей и использовать внутри него динамический ng-include. Предположим, у вас есть инжектор в $scope:

    <div ng-include="injector.get('session').role+'_dashboard.html'"></div>
    

    Таким образом, у вас должны быть представления user_dashboard.html и admin_dashboard.html. Внутри каждого можно применить отдельный контроллер, например user_dashboard.html:

    <div id="user_dashboard" ng-controller="UserDashboardCtrl">
        User markup
    </div>
    
    04.09.2014

    6

    Я использовал следующее решение (которое может быть не идеальным, но оно сработало для меня в таких сценариях):

    1. Укажите контроллер в самом шаблоне, используя ngController.

    2. Загрузите шаблон, используя общее имя представления (например, views/dashboard.html).

    3. Измените то, на что ссылается views/dashboard.html, используя $templateCache.put(...) всякий раз, когда изменяется роль вошедшего в систему пользователя.


    Вот урезанный пример подхода:

    app.controller('loginCtrl', function ($location, $scope, User) {
        ...
        $scope.loginAs = function (role) {
            // First set the user role
            User.setRole(role);
    
            // Then navigate to Dashboard
            $location.path('/dashboard');
        };
    });
    
    // A simplified `User` service that takes care of swapping templates,
    // based on the role. ("User" is probably not the best name...)
    app.service('User', function ($http, $templateCache) {
        var guestRole = 'guest';
        var facadeUrl = 'views/dashboard.html';
        var emptyTmpl = '';
        var errorTmpl = 'Failed to load template !';
        var tempTmpl  = 'Loading template...';
    
        ...
    
        // Upon logout, put an empty template into `$templateCache`
        this.logout = function () {
            this.role = guestRole;
            $templateCache.put(facadeUrl, emptyTmpl);
        };
    
        // When the role changes (e.g. upon login), set the role as well as the template
        // (remember that the template itself will specify the appropriate controller) 
        this.setRole = function (role) {
            this.role = role;
    
            // The actual template URL    
            var url = 'views/' + role + '/dashboard.html';
    
            // Put a temporary template into `$templateCache`
            $templateCache.put(facadeUrl, tempTmpl);
    
            // Fetch the actual template (from the `$templateCahce` if available)
            // and store it under the "generic" URL (`views/dashboard.html`)
            $http.get(url, {cache: $templateCache}).
                  success(function (tmpl) {
                      $templateCache.put(facadeUrl, tmpl);
                  }).
                  error(function () {
                      // Handle errors...
                      $templateCache.put(facadeUrl, errorTmpl);
                  });
        };
    
        // Initialize role and template        
        this.logout();
    });
    
    // When the user navigates to '/dashboard', load the `views/dashboard.html` template.
    // In a real app, you should of course verify that the user is logged in etc...
    // (Here I use `ngRoute` for simplicity, but you can use any routing module.)
    app.config(function ($routeProvider) {
        $routeProvider.
            when('/dashboard', {
                templateUrl: 'views/dashboard.html'
            }).
            ...
    });
    

    См. также эту короткую демонстрацию.
    ( Я использую ngRoute для простоты, но это не имеет какой-либо разницы, поскольку вся работа выполняется службой User.)

    31.08.2014

    7

    Здесь нет нужды в длинных пояснениях.

    Используйте разрешение и изменение $route.$$route.templateUrl или используйте routeChangeError, передав новый маршрут или соответствующий параметр обещанию.

    var md = angular.module('mymodule', ['ngRoute']);
    md.config(function($routeProvider, $locationProvider) {
        $routeProvider.when('/common_route/:someparam', {
            resolve: {
                nextRoute: function($q, $route, userService) {
                    defer = $q.defer()
                    userService.currentRole(function(data) { defer.reject({nextRoute: 'user_based_route/'+data) });
                    return defer.promise;
                }
            }
        });
        $rootScope.$on("$routeChangeError", function(evt, current, previous, rejection) {
          if (rejection.route) {
            return $location.path(rejection.route).replace();
          }
        });
    });
    
    03.09.2014

    8

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

    В этом методе я полностью разделяю URL-адреса маршрута и шаблона в зависимости от роли этого пользователя и перенаправляю пользователя на индексную страницу, если он находится на маршруте, на просмотр которого у него нет прав.

    С UI Router я в основном добавляю в состояние такой атрибут данных:

    .state('admin', {
                url: "/admin",
                templateUrl: "views/admin.html",
                data: {  requireRole: 'admin' }
            })
    

    Когда пользователь аутентифицирован, я сохраняю данные его роли в localstorage и $rootscope из контроллера следующим образом:

    var role = JSON.stringify(response.data); // response from api with role details
    
    // Set the stringified user data into local storage
    localStorage.setItem('role', role);
    
    // Putting the user's role on $rootScope for access by other controllers
    $rootScope.role = response.data;
    

    Наконец, я использую $stateChangeStart для проверки роли и перенаправления пользователя, если пользователь не должен просматривать страницу:

    .run(['$rootScope', '$state', function($rootScope, $state) {
    
            // $stateChangeStart is fired whenever the state changes. We can use some parameters
            // such as toState to hook into details about the state as it is changing
            $rootScope.$on('$stateChangeStart', function(event, toState) {
    
                    var role = JSON.parse(localStorage.getItem('role'));
                    $rootScope.role = role;
    
                    // Redirect user is NOT authenticated and accesing private pages
                    var requireRole = toState.data !== undefined
                                      && toState.data.requireRole;
    
                     if( (requireRole == 'admin' && role != 'admin')) )
                     {
                       $state.go('index');
                       event.preventDefault();
                       return;
                     }
         }
    
    });
    

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

    15.07.2015

    9

    Есть отличный проект https://github.com/Narzerus/angular-permission, ему нужен пользовательский интерфейс -маршрутизатор. Проект новый, тем не менее работает хорошо.

    08.07.2015
    Новые материалы

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

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..