ฉันกำลังอยู่ระหว่างการทำตัวอย่างที่ดีกว่ารวมถึงการทำความสะอาดบริการเหล่านี้ให้เป็นโมดูลที่ใช้งานได้ แต่นี่คือสิ่งที่ฉันได้ทำมา นี่เป็นกระบวนการที่ซับซ้อนในการหลีกเลี่ยงข้อควรระวังบางประการ คุณจะต้องแบ่งมันออกเป็นหลายชิ้น
ลองดูที่เสียงพึมพำนี้
ก่อนอื่นคุณต้องมีบริการเพื่อจัดเก็บข้อมูลประจำตัวของผู้ใช้ 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');
}
}
});
}
};
}
])
ตอนนี้สิ่งที่คุณต้องทำคือการฟังใน'sui-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();
});
}
]);
ส่วนที่ยุ่งยากเกี่ยวกับการติดตามข้อมูลประจำตัวของผู้ใช้คือการค้นหาถ้าคุณได้ทำการรับรองความถูกต้องแล้ว (เช่นคุณกำลังเยี่ยมชมหน้าเว็บหลังจากเซสชันก่อนหน้านี้และบันทึกโทเค็นการตรวจสอบสิทธิ์ในคุกกี้หรืออาจรีเฟรชหน้าเว็บยากหรือ วางลงบน 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
สมาชิกบทบาท สิ่งที่ได้รับอนุญาตทั้งหมดที่เรากล่าวถึงจะมาจากที่นั่น