这是使用或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");
});
};
}])