Если ваше приложение предполагает авторизацию пользователей и/или проверку прав доступа, то вам придется либо изобретать велосипед, либо гуглить в поисках подходящего решения. В принципе, я тоже это делал. В итоге я принял приемлемым для себя описанный ниже вариант.
Предпосылки
Информацию об авторизованном пользователе я решил хранить в 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-правила, а затем проверять их в своем сервисе.
Автор: victorburre