นี่เป็นอีกวิธีหนึ่งที่เป็นไปได้โดยใช้resolve
แอตทริบิวต์ของ$stateProvider
หรือ$routeProvider
. ตัวอย่างด้วย$stateProvider
:
.config(["$stateProvider", function ($stateProvider) {
$stateProvider
.state("forbidden", {
/* ... */
})
.state("signIn", {
/* ... */
resolve: {
access: ["Access", function (Access) { return Access.isAnonymous(); }],
}
})
.state("home", {
/* ... */
resolve: {
access: ["Access", function (Access) { return Access.isAuthenticated(); }],
}
})
.state("admin", {
/* ... */
resolve: {
access: ["Access", function (Access) { return Access.hasRole("ROLE_ADMIN"); }],
}
});
}])
Access
แก้ไขหรือปฏิเสธสัญญาขึ้นอยู่กับสิทธิ์ของผู้ใช้ปัจจุบัน:
.factory("Access", ["$q", "UserProfile", function ($q, UserProfile) {
var Access = {
OK: 200,
// "we don't know who you are, so we can't say if you're authorized to access
// this resource or not yet, please sign in first"
UNAUTHORIZED: 401,
// "we know who you are, and your profile does not allow you to access this resource"
FORBIDDEN: 403,
hasRole: function (role) {
return UserProfile.then(function (userProfile) {
if (userProfile.$hasRole(role)) {
return Access.OK;
} else if (userProfile.$isAnonymous()) {
return $q.reject(Access.UNAUTHORIZED);
} else {
return $q.reject(Access.FORBIDDEN);
}
});
},
hasAnyRole: function (roles) {
return UserProfile.then(function (userProfile) {
if (userProfile.$hasAnyRole(roles)) {
return Access.OK;
} else if (userProfile.$isAnonymous()) {
return $q.reject(Access.UNAUTHORIZED);
} else {
return $q.reject(Access.FORBIDDEN);
}
});
},
isAnonymous: function () {
return UserProfile.then(function (userProfile) {
if (userProfile.$isAnonymous()) {
return Access.OK;
} else {
return $q.reject(Access.FORBIDDEN);
}
});
},
isAuthenticated: function () {
return UserProfile.then(function (userProfile) {
if (userProfile.$isAuthenticated()) {
return Access.OK;
} else {
return $q.reject(Access.UNAUTHORIZED);
}
});
}
};
return Access;
}])
UserProfile
สำเนาคุณสมบัติที่ผู้ใช้ปัจจุบันและดำเนินการ$hasRole
, $hasAnyRole
, $isAnonymous
และ$isAuthenticated
วิธีการตรรกะ (บวก$refresh
วิธีการอธิบายในภายหลัง):
.factory("UserProfile", ["Auth", function (Auth) {
var userProfile = {};
var clearUserProfile = function () {
for (var prop in userProfile) {
if (userProfile.hasOwnProperty(prop)) {
delete userProfile[prop];
}
}
};
var fetchUserProfile = function () {
return Auth.getProfile().then(function (response) {
clearUserProfile();
return angular.extend(userProfile, response.data, {
$refresh: fetchUserProfile,
$hasRole: function (role) {
return userProfile.roles.indexOf(role) >= 0;
},
$hasAnyRole: function (roles) {
return !!userProfile.roles.filter(function (role) {
return roles.indexOf(role) >= 0;
}).length;
},
$isAnonymous: function () {
return userProfile.anonymous;
},
$isAuthenticated: function () {
return !userProfile.anonymous;
}
});
});
};
return fetchUserProfile();
}])
Auth
รับผิดชอบในการร้องขอเซิร์ฟเวอร์เพื่อทราบโปรไฟล์ผู้ใช้ (เชื่อมโยงกับโทเค็นการเข้าถึงที่แนบมากับคำขอเป็นต้น):
.service("Auth", ["$http", function ($http) {
this.getProfile = function () {
return $http.get("api/auth");
};
}])
เซิร์ฟเวอร์คาดว่าจะส่งคืนอ็อบเจ็กต์ JSON ดังกล่าวเมื่อร้องขอGET api/auth
:
{
"name": "John Doe", // plus any other user information
"roles": ["ROLE_ADMIN", "ROLE_USER"], // or any other role (or no role at all, i.e. an empty array)
"anonymous": false // or true
}
สุดท้ายเมื่อAccess
ปฏิเสธสัญญาถ้าใช้ui.router
ใน$stateChangeError
กรณีที่จะถูกไล่ออก:
.run(["$rootScope", "Access", "$state", "$log", function ($rootScope, Access, $state, $log) {
$rootScope.$on("$stateChangeError", function (event, toState, toParams, fromState, fromParams, error) {
switch (error) {
case Access.UNAUTHORIZED:
$state.go("signIn");
break;
case Access.FORBIDDEN:
$state.go("forbidden");
break;
default:
$log.warn("$stateChangeError event catched");
break;
}
});
}])
ถ้าใช้ngRoute
ใน$routeChangeError
กรณีที่จะถูกไล่ออก:
.run(["$rootScope", "Access", "$location", "$log", function ($rootScope, Access, $location, $log) {
$rootScope.$on("$routeChangeError", function (event, current, previous, rejection) {
switch (rejection) {
case Access.UNAUTHORIZED:
$location.path("/signin");
break;
case Access.FORBIDDEN:
$location.path("/forbidden");
break;
default:
$log.warn("$stateChangeError event catched");
break;
}
});
}])
โปรไฟล์ผู้ใช้ยังสามารถเข้าถึงได้ในคอนโทรลเลอร์:
.state("home", {
/* ... */
controller: "HomeController",
resolve: {
userProfile: "UserProfile"
}
})
UserProfile
จากนั้นจะมีคุณสมบัติที่ส่งคืนโดยเซิร์ฟเวอร์เมื่อร้องขอGET api/auth
:
.controller("HomeController", ["$scope", "userProfile", function ($scope, userProfile) {
$scope.title = "Hello " + userProfile.name; // "Hello John Doe" in the example
}])
UserProfile
จะต้องมีการรีเฟรชเมื่อผู้ใช้ลงชื่อเข้าหรือออกเพื่อให้Access
สามารถจัดการเส้นทางด้วยโปรไฟล์ผู้ใช้ใหม่ได้ UserProfile.$refresh()
คุณสามารถโหลดทั้งหน้าหรือโทร ตัวอย่างเมื่อลงชื่อเข้าใช้:
.service("Auth", ["$http", function ($http) {
/* ... */
this.signIn = function (credentials) {
return $http.post("api/auth", credentials).then(function (response) {
// authentication succeeded, store the response access token somewhere (if any)
});
};
}])
.state("signIn", {
/* ... */
controller: "SignInController",
resolve: {
/* ... */
userProfile: "UserProfile"
}
})
.controller("SignInController", ["$scope", "$state", "Auth", "userProfile", function ($scope, $state, Auth, userProfile) {
$scope.signIn = function () {
Auth.signIn($scope.credentials).then(function () {
// user successfully authenticated, refresh UserProfile
return userProfile.$refresh();
}).then(function () {
// UserProfile is refreshed, redirect user somewhere
$state.go("home");
});
};
}])