Я нахожусь в процессе создания хорошей демонстрации, а также превращения некоторых из этих сервисов в полезный модуль, но вот что я придумала. Это сложный процесс для обхода некоторых предостережений, так что оставайтесь там. Вам нужно будет разбить это на несколько частей.
Взгляни на этот слон .
Во-первых, вам нужен сервис для хранения личности пользователя. Я называю это principal
. Это может быть проверено, чтобы видеть, вошел ли пользователь в систему, и по запросу, это может разрешить объект, который представляет важную информацию об идентичности пользователя. Это может быть все, что вам нужно, но основными являются отображаемое имя, имя пользователя, возможно, адрес электронной почты и роли, к которым принадлежит пользователь (если это относится к вашему приложению). У принципала также есть методы для проверки ролей.
.factory('principal', ['$q', '$http', '$timeout',
function($q, $http, $timeout) {
var _identity = undefined,
_authenticated = false;
return {
isIdentityResolved: function() {
return angular.isDefined(_identity);
},
isAuthenticated: function() {
return _authenticated;
},
isInRole: function(role) {
if (!_authenticated || !_identity.roles) return false;
return _identity.roles.indexOf(role) != -1;
},
isInAnyRole: function(roles) {
if (!_authenticated || !_identity.roles) return false;
for (var i = 0; i < roles.length; i++) {
if (this.isInRole(roles[i])) return true;
}
return false;
},
authenticate: function(identity) {
_identity = identity;
_authenticated = identity != null;
},
identity: function(force) {
var deferred = $q.defer();
if (force === true) _identity = undefined;
// check and see if we have retrieved the
// identity data from the server. if we have,
// reuse it by immediately resolving
if (angular.isDefined(_identity)) {
deferred.resolve(_identity);
return deferred.promise;
}
// otherwise, retrieve the identity data from the
// server, update the identity object, and then
// resolve.
// $http.get('/svc/account/identity',
// { ignoreErrors: true })
// .success(function(data) {
// _identity = data;
// _authenticated = true;
// deferred.resolve(_identity);
// })
// .error(function () {
// _identity = null;
// _authenticated = false;
// deferred.resolve(_identity);
// });
// for the sake of the demo, fake the lookup
// by using a timeout to create a valid
// fake identity. in reality, you'll want
// something more like the $http request
// commented out above. in this example, we fake
// looking up to find the user is
// not logged in
var self = this;
$timeout(function() {
self.authenticate(null);
deferred.resolve(_identity);
}, 1000);
return deferred.promise;
}
};
}
])
Во-вторых, вам нужна служба, которая проверяет состояние, в которое пользователь хочет перейти, проверяет, вошли ли они в систему (при необходимости; не требуется для входа, сброса пароля и т. Д.), А затем выполняет проверку роли (если ваше приложение). нужно это). Если они не аутентифицированы, отправьте их на страницу входа. Если они прошли проверку подлинности, но не прошли проверку роли, отправьте их на страницу отказа в доступе. Я называю эту услугу authorization
.
.factory('authorization', ['$rootScope', '$state', 'principal',
function($rootScope, $state, principal) {
return {
authorize: function() {
return principal.identity()
.then(function() {
var isAuthenticated = principal.isAuthenticated();
if ($rootScope.toState.data.roles
&& $rootScope.toState
.data.roles.length > 0
&& !principal.isInAnyRole(
$rootScope.toState.data.roles))
{
if (isAuthenticated) {
// user is signed in but not
// authorized for desired state
$state.go('accessdenied');
} else {
// user is not authenticated. Stow
// the state they wanted before you
// send them to the sign-in state, so
// you can return them when you're done
$rootScope.returnToState
= $rootScope.toState;
$rootScope.returnToStateParams
= $rootScope.toStateParams;
// now, send them to the signin state
// so they can log in
$state.go('signin');
}
}
});
}
};
}
])
Теперь все , что вам нужно сделать , это слушать в ui-router
«с $stateChangeStart
. Это дает вам возможность проверить текущее состояние, состояние, в которое они хотят перейти, и вставить вашу проверку авторизации. Если это не удается, вы можете отменить переход по маршруту или перейти на другой маршрут.
.run(['$rootScope', '$state', '$stateParams',
'authorization', 'principal',
function($rootScope, $state, $stateParams,
authorization, principal)
{
$rootScope.$on('$stateChangeStart',
function(event, toState, toStateParams)
{
// track the state the user wants to go to;
// authorization service needs this
$rootScope.toState = toState;
$rootScope.toStateParams = toStateParams;
// if the principal is resolved, do an
// authorization check immediately. otherwise,
// it'll be done when the state it resolved.
if (principal.isIdentityResolved())
authorization.authorize();
});
}
]);
Сложная задача по отслеживанию личности пользователя - поиск, если вы уже прошли аутентификацию (например, вы посещаете страницу после предыдущего сеанса и сохранили токен аутентификации в файле cookie, или, может быть, вы сильно обновили страницу, или упал на URL из ссылки). Из-за того, как это ui-router
работает, вам нужно выполнить идентификацию один раз перед проверкой аутентификации. Вы можете сделать это, используя resolve
опцию в конфигурации вашего состояния. У меня есть одно родительское состояние для сайта, от которого наследуются все состояния, что заставляет принципал быть разрешенным прежде, чем что-либо еще произойдет.
$stateProvider.state('site', {
'abstract': true,
resolve: {
authorize: ['authorization',
function(authorization) {
return authorization.authorize();
}
]
},
template: '<div ui-view />'
})
Здесь есть другая проблема ... resolve
вызывается только один раз. Как только ваше обещание поиска личности будет выполнено, он больше не будет запускать делегат разрешения. Таким образом, мы должны выполнить ваши проверки подлинности в двух местах: один раз в соответствии с вашим обещанием идентификации resolve
, которое охватывает первый раз, когда ваше приложение загружается, и один раз в$stateChangeStart
если разрешение было выполнено, которое охватывает любое время, когда вы переходите между состояниями.
Хорошо, так что мы сделали до сих пор?
- Мы проверяем, когда приложение загружается, если пользователь вошел в систему.
- Мы отслеживаем информацию о зарегистрированном пользователе.
- Мы перенаправляем их в состояние входа для состояний, требующих входа пользователя.
- Мы перенаправляем их в состояние отказа в доступе, если у них нет прав доступа к нему.
- У нас есть механизм для перенаправления пользователей обратно в исходное состояние, которое они запрашивали, если нам нужно, чтобы они вошли в систему.
- Мы можем вывести пользователя из системы (необходимо подключить его к любому клиентскому или серверному коду, который управляет вашим авторизационным билетом).
- Нам не нужно отправлять пользователей обратно на страницу входа каждый раз, когда они перезагружают свой браузер или переходят по ссылке.
Куда мы отправимся отсюда? Ну, вы можете организовать свои состояния в регионы , которые требуют войти. Вы можете потребовать проверку подлинности / авторизованных пользователей, добавляя data
с roles
этими состояниями (или одного из родителей из них, если вы хотите использовать наследование). Здесь мы ограничиваем ресурс администраторами:
.state('restricted', {
parent: 'site',
url: '/restricted',
data: {
roles: ['Admin']
},
views: {
'content@': {
templateUrl: 'restricted.html'
}
}
})
Теперь вы можете контролировать состояние за состоянием, что пользователи могут получить доступ к маршруту. Любые другие проблемы? Может быть, меняется только часть представления в зависимости от того, вошли они или нет? Нет проблем. Используйте principal.isAuthenticated()
или дажеprincipal.isInRole()
с любым из многочисленных способов условного отображения шаблона или элемента.
Сначала вставьте principal
в контроллер или что-то еще, и прикрепите его к области видимости, чтобы вы могли легко использовать его в своем представлении:
.scope('HomeCtrl', ['$scope', 'principal',
function($scope, principal)
{
$scope.principal = principal;
});
Показать или скрыть элемент:
<div ng-show="principal.isAuthenticated()">
I'm logged in
</div>
<div ng-hide="principal.isAuthenticated()">
I'm not logged in
</div>
И так далее, и так далее, и тому подобное. В любом случае, в вашем примере приложения у вас будет состояние для домашней страницы, которое позволит неаутентифицированным пользователям заходить. У них могут быть ссылки на состояния входа или регистрации или встроенные формы на этой странице. Все, что тебе подходит.
Все страницы панели мониторинга могут наследоваться от состояния, в котором требуется, чтобы пользователи вошли в систему и, скажем, были User
членами роли. Все авторизационные материалы, которые мы обсуждали, будут исходить оттуда.