路由器登录认证
我是AngularJS的新手,我对如何在以下场景中使用angular-“ui-router”感到困惑:
我正在构建一个由两部分组成的Web应用程序。 第一部分是具有登录和注册视图的主页,第二部分是仪表板(成功登录后)。
我为home部分创建了一个index.html
,它的angular app和ui-router
配置来处理/login
和/signup
视图,还有另一个文件dashboard.html
用于仪表板部分的app和ui-router
配置处理很多子视图。
现在我完成了仪表板部分,不知道如何将两个部分与不同的角度应用程序结合起来。 我怎么能告诉家庭应用程序重定向到仪表板应用程序?
我正在制作一个更好的演示过程,并将这些服务中的一部分清理成可用的模块,但以下是我想到的。 这是一个复杂的过程来解决一些警告,所以挂在那里。 你需要把它分成几块。
看看这个庞然大物。
首先,您需要一项服务来存储用户的身份。 我打电话给这位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');
}
}
});
}
};
}
])
现在,您只需在ui-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();
});
}
]);
关于跟踪用户身份的棘手部分是,如果您已经通过身份验证(例如,您在前一个会话之后访问该页面,并在Cookie中保存了身份验证令牌,或者您可能刷新了页面,或者从一个链接放到一个URL上)。 由于ui-router
工作方式,您需要在认证检查之前进行一次身份验证。 您可以使用状态配置中的resolve
选项来执行此操作。 我有一个所有状态继承的站点的父状态,这会在发生任何事情之前强制主体被解决。
$stateProvider.state('site', {
'abstract': true,
resolve: {
authorize: ['authorization',
function(authorization) {
return authorization.authorize();
}
]
},
template: '<div ui-view />'
})
这里还有另一个问题...... resolve
只会被调用一次。 一旦您的身份查找承诺完成,它将不会再运行解析委托。 因此,我们必须在两个地方进行身份验证检查:一次根据您的身份承诺解决resolve
,其中包含第一次加载应用程序的时间,以及一次在$stateChangeStart
如果分辨率已完成,则可以在任何时候浏览状态。
好的,到目前为止我们做了什么?
我们从哪里出发? 那么,您可以将您的状态组织到需要登录的区域中。您可以通过向这些状态添加具有roles
data
(如果您想使用继承,则可以要求授权用户)。 在这里,我们将资源限制为管理员:
.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
角色成员。 我们讨论的所有授权内容都将从此处流动。
迄今为止发布的解决方案在我看来是不必要的复杂。 有一个更简单的方法。 ui-router
的文档说听$locationChangeSuccess
并使用$urlRouter.sync()
来检查状态转换,暂停或恢复它。 但即使这实际上不起作用。
但是,这里有两个简单的选择。 选一个:
解决方案1:侦听$locationChangeSuccess
你可以听$locationChangeSuccess
,你可以执行一些逻辑,甚至是那里的异步逻辑。 基于该逻辑,您可以让函数返回undefined,这会导致状态转换继续正常,或者如果用户需要进行身份验证,则可以执行$state.go('logInPage')
。 这是一个例子:
angular.module('App', ['ui.router'])
// In the run phase of your Angular application
.run(function($rootScope, user, $state) {
// Listen to '$locationChangeSuccess', not '$stateChangeStart'
$rootScope.$on('$locationChangeSuccess', function() {
user
.logIn()
.catch(function() {
// log-in promise failed. Redirect to log-in page.
$state.go('logInPage')
})
})
})
请记住,这实际上并不阻止加载目标状态,但如果用户未经授权,它会重定向到登录页面。 无所谓,因为真正的保护在服务器上。
解决方案2:使用状态resolve
在此解决方案中,您使用ui-router
解析功能。
如果用户未通过身份验证,然后将其重定向到登录页面,则基本上拒绝resolve
承诺。
这是怎么回事:
angular.module('App', ['ui.router'])
.config(
function($stateProvider) {
$stateProvider
.state('logInPage', {
url: '/logInPage',
templateUrl: 'sections/logInPage.html',
controller: 'logInPageCtrl',
})
.state('myProtectedContent', {
url: '/myProtectedContent',
templateUrl: 'sections/myProtectedContent.html',
controller: 'myProtectedContentCtrl',
resolve: { authenticate: authenticate }
})
.state('alsoProtectedContent', {
url: '/alsoProtectedContent',
templateUrl: 'sections/alsoProtectedContent.html',
controller: 'alsoProtectedContentCtrl',
resolve: { authenticate: authenticate }
})
function authenticate($q, user, $state, $timeout) {
if (user.isAuthenticated()) {
// Resolve the promise successfully
return $q.when()
} else {
// The next bit of code is asynchronously tricky.
$timeout(function() {
// This code runs after the authentication promise has been rejected.
// Go to the log-in page
$state.go('logInPage')
})
// Reject the authentication promise to prevent the state from loading
return $q.reject()
}
}
}
)
与第一种解决方案不同,此解决方案实际上可防止目标状态加载。
最简单的解决方案是使用$stateChangeStart
和event.preventDefault()
在用户未通过身份验证时取消状态更改并将其重定向到作为登录页面的身份验证状态。
angular
.module('myApp', [
'ui.router',
])
.run(['$rootScope', 'User', '$state',
function ($rootScope, User, $state) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
if (toState.name !== 'auth' && !User.authenticaded()) {
event.preventDefault();
$state.go('auth');
}
});
}]
);
链接地址: http://www.djcxy.com/p/40707.html