Tôi đang trong quá trình tạo bản demo đẹp hơn cũng như dọn dẹp một số dịch vụ này thành một mô-đun có thể sử dụng được, nhưng đây là những gì tôi nghĩ ra. Đây là một quá trình phức tạp để làm việc xung quanh một số hãy cẩn thận, vì vậy hãy chờ đợi ở đó. Bạn sẽ cần phải chia nó thành nhiều phần.
Hãy nhìn vào plunk này .
Đầu tiên, bạn cần một dịch vụ để lưu trữ danh tính của người dùng. Tôi gọi đây principal
. Nó có thể được kiểm tra để xem người dùng đã đăng nhập chưa, và theo yêu cầu, nó có thể giải quyết một đối tượng thể hiện thông tin cần thiết về danh tính của người dùng. Đây có thể là bất cứ điều gì bạn cần, nhưng các yếu tố cần thiết sẽ là tên hiển thị, tên người dùng, có thể là email và vai trò của người dùng (nếu điều này áp dụng cho ứng dụng của bạn). Hiệu trưởng cũng có phương pháp để kiểm tra vai trò.
.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;
}
};
}
])
Thứ hai, bạn cần một dịch vụ kiểm tra trạng thái mà người dùng muốn đến, đảm bảo họ đã đăng nhập (nếu cần thiết, không cần thiết để đăng nhập, đặt lại mật khẩu, v.v.), sau đó thực hiện kiểm tra vai trò (nếu ứng dụng của bạn cần cái này). Nếu chúng không được xác thực, hãy gửi chúng đến trang đăng nhập. Nếu chúng được xác thực, nhưng không kiểm tra vai trò, hãy gửi chúng đến trang bị từ chối truy cập. Tôi gọi dịch vụ này 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');
}
}
});
}
};
}
])
Bây giờ tất cả bạn cần làm là lắng nghe trên ui-router
's $stateChangeStart
. Điều này cho bạn cơ hội kiểm tra trạng thái hiện tại, trạng thái họ muốn đến và chèn kiểm tra ủy quyền của bạn. Nếu thất bại, bạn có thể hủy quá trình chuyển tuyến hoặc thay đổi tuyến khác.
.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();
});
}
]);
Phần khó khăn trong việc theo dõi danh tính của người dùng là tìm kiếm nếu bạn đã xác thực (giả sử bạn đang truy cập trang sau một phiên trước đó và lưu mã thông báo xác thực trong cookie hoặc có thể bạn đã làm mới một trang hoặc rơi vào một URL từ một liên kết). Do cách thức ui-router
hoạt động, bạn cần giải quyết danh tính của mình một lần, trước khi kiểm tra xác thực của bạn. Bạn có thể làm điều này bằng cách sử dụng resolve
tùy chọn trong cấu hình trạng thái của bạn. Tôi có một trạng thái cha mẹ cho trang web mà tất cả các tiểu bang được thừa kế từ đó buộc hiệu trưởng phải được giải quyết trước khi có bất kỳ điều gì khác xảy ra.
$stateProvider.state('site', {
'abstract': true,
resolve: {
authorize: ['authorization',
function(authorization) {
return authorization.authorize();
}
]
},
template: '<div ui-view />'
})
Có một vấn đề khác ở đây ... resolve
chỉ được gọi một lần. Khi lời hứa tìm kiếm danh tính của bạn hoàn tất, nó sẽ không chạy lại ủy nhiệm giải quyết. Vì vậy, chúng tôi phải kiểm tra xác thực của bạn ở hai nơi: một lần theo lời hứa nhận dạng của bạn, giải quyết resolve
lần đầu tiên ứng dụng của bạn tải và một lần $stateChangeStart
nếu giải quyết xong, bao gồm bất kỳ lúc nào bạn điều hướng quanh các tiểu bang.
OK, vậy chúng ta đã làm gì cho đến nay?
- Chúng tôi kiểm tra xem khi tải ứng dụng nếu người dùng đăng nhập.
- Chúng tôi theo dõi thông tin về người dùng đã đăng nhập.
- Chúng tôi chuyển hướng họ để đăng nhập vào trạng thái yêu cầu người dùng đăng nhập.
- Chúng tôi chuyển hướng họ đến trạng thái từ chối truy cập nếu họ không có quyền truy cập.
- Chúng tôi có một cơ chế để chuyển hướng người dùng trở lại trạng thái ban đầu họ yêu cầu, nếu chúng tôi cần họ đăng nhập.
- Chúng tôi có thể đăng xuất người dùng (cần được kết nối đồng bộ với bất kỳ mã máy khách hoặc mã máy chủ nào quản lý vé xác thực của bạn).
- Chúng tôi không cần phải gửi người dùng trở lại trang đăng nhập mỗi khi họ tải lại trình duyệt của họ hoặc thả vào một liên kết.
Chúng ta sẽ đi đâu từ đây? Vâng, bạn có thể tổ chức các quốc gia của bạn vào khu vực mà yêu cầu đăng nhập. Bạn có thể yêu cầu chứng thực người dùng / ủy quyền bằng cách thêm data
với roles
những tiểu bang (hoặc cha mẹ của họ, nếu bạn muốn sử dụng thừa kế). Ở đây, chúng tôi giới hạn tài nguyên cho Quản trị viên:
.state('restricted', {
parent: 'site',
url: '/restricted',
data: {
roles: ['Admin']
},
views: {
'content@': {
templateUrl: 'restricted.html'
}
}
})
Bây giờ bạn có thể kiểm soát từng tiểu bang những gì người dùng có thể truy cập một tuyến đường. Bất kỳ mối quan tâm khác? Có lẽ chỉ khác nhau một phần của chế độ xem dựa trên việc họ có đăng nhập hay không? Không vấn đề gì. Sử dụng principal.isAuthenticated()
hoặc thậm chí principal.isInRole()
với bất kỳ cách nào trong số nhiều cách bạn có thể hiển thị một cách có điều kiện một mẫu hoặc một phần tử.
Đầu tiên, đưa principal
vào bộ điều khiển hoặc bất cứ thứ gì, và gắn nó vào phạm vi để bạn có thể sử dụng nó dễ dàng trong chế độ xem của mình:
.scope('HomeCtrl', ['$scope', 'principal',
function($scope, principal)
{
$scope.principal = principal;
});
Hiển thị hoặc ẩn một phần tử:
<div ng-show="principal.isAuthenticated()">
I'm logged in
</div>
<div ng-hide="principal.isAuthenticated()">
I'm not logged in
</div>
V.v., v.v. Dù sao, trong ứng dụng ví dụ của bạn, bạn sẽ có một trạng thái cho trang chủ sẽ cho phép người dùng không được xác thực ghé qua. Họ có thể có các liên kết đến trạng thái đăng nhập hoặc đăng ký hoặc có các biểu mẫu được tích hợp trong trang đó. Bất cứ điều gì phù hợp với bạn.
Tất cả các trang bảng điều khiển đều có thể kế thừa từ trạng thái yêu cầu người dùng phải đăng nhập và, giả sử, là User
thành viên vai trò. Tất cả những thứ ủy quyền mà chúng ta đã thảo luận sẽ chảy từ đó.