Jestem w trakcie tworzenia ładniejszej wersji demo, a także porządkowania niektórych z tych usług w użytecznym module, ale oto co wymyśliłem. Jest to złożony proces obejścia niektórych zastrzeżeń, więc trzymaj się. Musisz to rozbić na kilka części.
Spójrz na to słowo .
Po pierwsze, potrzebujesz usługi do przechowywania tożsamości użytkownika. Nazywam to principal
. Można to sprawdzić, czy użytkownik jest zalogowany, a na żądanie może rozwiązać obiekt reprezentujący niezbędne informacje o tożsamości użytkownika. Może to być wszystko, czego potrzebujesz, ale najważniejsze to nazwa wyświetlana, nazwa użytkownika, ewentualnie e-mail i role, do których użytkownik należy (jeśli dotyczy to Twojej aplikacji). Principal ma również metody sprawdzania ról.
.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;
}
};
}
])
Po drugie, potrzebujesz usługi, która sprawdza stan, w którym użytkownik chce przejść, upewnia się, że jest zalogowany (jeśli to konieczne; nie jest konieczne do logowania, resetowania hasła itp.), A następnie sprawdza rolę (jeśli Twoja aplikacja jest zalogowana) potrzebuje tego). Jeśli nie są uwierzytelnione, wyślij je na stronę logowania. Jeśli są uwierzytelnione, ale nie sprawdzają roli, wyślij je na stronę, na której odmówiono dostępu. Dzwonię do tej usługi 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');
}
}
});
}
};
}
])
Teraz wszystko, co musisz zrobić, to słuchać na ui-router
„s $stateChangeStart
. Daje to szansę na sprawdzenie bieżącego stanu, stanu, do którego chcą przejść, i wstawienie kontroli autoryzacji. Jeśli się nie powiedzie, możesz anulować zmianę trasy lub zmienić inną trasę.
.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();
});
}
]);
Trudną częścią śledzenia tożsamości użytkownika jest sprawdzenie go, jeśli już go uwierzytelniłeś (powiedzmy, że odwiedzasz stronę po poprzedniej sesji i zapisałeś token uwierzytelnienia w pliku cookie, a może mocno odświeżyłeś stronę lub upuszczony na adres URL z linku). Ze względu na sposób ui-router
działania konieczne jest jednokrotne rozpoznanie tożsamości przed sprawdzeniem autoryzacji. Możesz to zrobić za pomocą resolve
opcji w konfiguracji stanu. Mam jeden stan nadrzędny dla witryny, z której dziedziczą wszystkie stany, co zmusza zasadę do rozwiązania, zanim cokolwiek innego się wydarzy.
$stateProvider.state('site', {
'abstract': true,
resolve: {
authorize: ['authorization',
function(authorization) {
return authorization.authorize();
}
]
},
template: '<div ui-view />'
})
Jest tu inny problem ... resolve
zostaje wywołany tylko raz. Po wypełnieniu obietnicy wyszukiwania tożsamości nie uruchomi ona ponownie delegata rozwiązania. Musimy więc przeprowadzić weryfikację uwierzytelnienia w dwóch miejscach: raz, zgodnie z obietnicą tożsamości, w resolve
której dotyczy to pierwszego uruchomienia aplikacji, i raz, $stateChangeStart
jeśli rozdzielczość została wykonana, co obejmuje każdą nawigację po stanach.
OK, więc co do tej pory zrobiliśmy?
- Sprawdzamy, kiedy aplikacja się ładuje, jeśli użytkownik jest zalogowany.
- Śledzimy informacje o zalogowanym użytkowniku.
- Przekierowujemy ich do stanu logowania dla stanów wymagających zalogowania użytkownika.
- Przekierowujemy ich do stanu odmowy dostępu, jeśli nie mają autoryzacji dostępu.
- Mamy mechanizm przekierowujący użytkowników z powrotem do pierwotnego stanu, o który prosili, jeśli potrzebowaliśmy ich do zalogowania się.
- Możemy wylogować użytkownika (musi być połączony z dowolnym kodem klienta lub serwera, który zarządza twoim biletem autoryzacyjnym).
- My nie potrzebujemy do wysyłania użytkownikom powrót do strony logowania za każdym razem przeładować swoją przeglądarkę lub spadek na link.
Dokąd zmierzamy? Cóż, można organizować swoje stany w regionach, które wymagają podpisania w. Można wymagać uwierzytelnionych / autoryzowanych użytkowników, dodając data
z roles
tych państw (lub rodzic z nich, jeśli chcesz korzystać z dziedziczenia). Tutaj ograniczamy zasób do administratorów:
.state('restricted', {
parent: 'site',
url: '/restricted',
data: {
roles: ['Admin']
},
views: {
'content@': {
templateUrl: 'restricted.html'
}
}
})
Teraz możesz kontrolować stan po stanie, którzy użytkownicy mogą uzyskać dostęp do trasy. Jakieś inne obawy? Może zmieniając tylko część widoku w zależności od tego, czy są zalogowani? Nie ma problemu. Użyj principal.isAuthenticated()
lubprincipal.isInRole()
z jednego z wielu sposobów warunkowego wyświetlania szablonu lub elementu, z nich.
Najpierw wstrzyknij principal
do kontrolera lub cokolwiek innego i przyklej go do lunety, abyś mógł go łatwo używać w swoim widoku:
.scope('HomeCtrl', ['$scope', 'principal',
function($scope, principal)
{
$scope.principal = principal;
});
Pokaż lub ukryj element:
<div ng-show="principal.isAuthenticated()">
I'm logged in
</div>
<div ng-hide="principal.isAuthenticated()">
I'm not logged in
</div>
Itd., Itd. Itd. Tak czy inaczej, w twojej przykładowej aplikacji miałbyś stan strony głównej, który pozwalałby odwiedzać nieuwierzytelnionym użytkownikom. Mogą mieć linki do stanów logowania lub rejestracji albo mieć wbudowane formularze na tej stronie. Cokolwiek Ci odpowiada.
Wszystkie strony panelu kontrolnego mogą dziedziczyć po stanie, który wymaga zalogowania użytkowników i, powiedzmy, bycia User
członkiem roli. Stąd płyną wszystkie omówione przez nas kwestie związane z autoryzacją.