Javascript AngularJS ui路由器登录验证
我是AngularJS的新手,我对如何在以下场景中使用AngularJS“ui路由器”有点困惑: 我正在构建一个由两部分组成的web应用程序。第一部分是主页及其登录和注册视图,第二部分是仪表板(成功登录后) 我已经为home部分创建了一个Javascript AngularJS ui路由器登录验证,javascript,angularjs,angular-ui-router,Javascript,Angularjs,Angular Ui Router,我是AngularJS的新手,我对如何在以下场景中使用AngularJS“ui路由器”有点困惑: 我正在构建一个由两部分组成的web应用程序。第一部分是主页及其登录和注册视图,第二部分是仪表板(成功登录后) 我已经为home部分创建了一个index.html,其中包含angular应用程序和ui路由器config,用于处理/login和/signup视图, 还有另一个文件dashboard.html用于dashboard部分及其应用程序和UIRouterconfig,用于处理多个子视图 现在我完
index.html
,其中包含angular应用程序和ui路由器
config,用于处理/login
和/signup
视图,
还有另一个文件dashboard.html
用于dashboard部分及其应用程序和UIRouter
config,用于处理多个子视图
现在我完成了仪表板部分,不知道如何将这两个部分与它们不同的角度应用程序结合起来。我怎样才能告诉home应用程序重定向到dashboard应用程序?我认为您需要一个处理身份验证过程(及其存储)的
服务
在此服务中,您需要一些基本方法:
isAuthenticated()
login()
logout()
- 等等
此服务应注入每个模块的控制器中:
- 在仪表板部分,使用此服务检查用户是否经过身份验证(
service.isAuthenticated()
method)。如果没有,则重定向到/login
- 在登录部分,只需使用表单数据通过
service.login()方法对用户进行身份验证
这一行为的一个好的和健壮的例子是项目,特别是它基于awesome
希望这能有所帮助我正在制作一个更好的演示,并将其中一些服务清理成一个可用的模块,但下面是我的想法。这是一个复杂的过程,需要解决一些警告,所以请坚持下去。你需要把它分成几部分
首先,您需要一个服务来存储用户的身份。我称之为主体
。可以检查用户是否已登录,并根据请求解析表示用户身份基本信息的对象。这可以是您需要的任何内容,但基本要素将是显示名称、用户名、可能的电子邮件以及用户所属的角色(如果这适用于您的应用程序)。主体还具有执行角色检查的方法
.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;
}
};
}
])
现在,您需要做的就是在ui路由器上收听。这使您有机会检查当前状态,即他们想要进入的状态,并插入授权检查。如果失败,您可以取消路由转换,或更改为其他路由
.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路由器的工作方式,您需要在身份验证检查之前进行一次身份解析。您可以使用状态配置中的resolve
选项执行此操作。我有一个所有状态继承自的站点的父状态,这迫使主体在其他任何事情发生之前被解析
$stateProvider.state('site', {
'abstract': true,
resolve: {
authorize: ['authorization',
function(authorization) {
return authorization.authorize();
}
]
},
template: '<div ui-view />'
})
现在,您可以逐州控制用户可以访问的路由。还有其他问题吗?可能只是根据是否登录而改变视图的一部分?没问题。使用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>
我已经登录了
我没有登录
等等,等等,等等。无论如何,在您的示例应用程序中,您的主页会有一个状态,允许未经身份验证的用户访问。他们可以有登录或注册状态的链接,或者在该页面中内置这些表单。随便你喜欢什么
仪表板页面可以全部继承自一个状态,该状态要求用户登录,例如,成为用户
角色成员。我们讨论过的所有授权内容都将从这里开始。以下是我们如何摆脱无限路由循环,仍然使用$state.go
而不是$location.path
if('401' !== toState.name) {
if (principal.isIdentityResolved()) authorization.authorize();
}
我创建这个模块是为了让这个过程变得简单
你可以这样做:
$routeProvider
.state('secret',
{
...
permissions: {
only: ['admin', 'god']
}
});
或者也
$routeProvider
.state('userpanel',
{
...
permissions: {
except: ['not-logged-in']
}
});
这是全新的,但值得一看
在我看来,到目前为止发布的解决方案是不必要的复杂。有一个更简单的方法。用户说,请收听$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
在这个解决方案中,您使用
如果用户未通过身份验证,则基本上拒绝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()
}
}
}
)
与第一个解决方案不同,此解决方案实际上可以防止
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')
})
})
})
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()
}
}
}
)
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');
}
});
}]
);
$urlRouterProvider.rule(function ($injector, $location) {
var UserService = $injector.get('UserService');
var path = $location.path(), normalized = path.toLowerCase();
if (!UserService.isLoggedIn() && path.indexOf('login') === -1) {
$location.path('/login/signin');
}
});
/login/signup // registering new user
/login/signin // login to app
export default (
$stateProvider,
$locationProvider,
$urlRouterProvider
) => {
$stateProvider.state('login', {
parent: 'app',
url: '/login',
abstract: true,
template: '<ui-view></ui-view>'
})
$stateProvider.state('signin', {
parent: 'login',
url: '/signin',
template: '<login-signin-directive></login-signin-directive>'
});
$stateProvider.state('lock', {
parent: 'login',
url: '/lock',
template: '<login-lock-directive></login-lock-directive>'
});
$stateProvider.state('signup', {
parent: 'login',
url: '/signup',
template: '<login-signup-directive></login-signup-directive>'
});
$urlRouterProvider.rule(function ($injector, $location) {
var UserService = $injector.get('UserService');
var path = $location.path();
if (!UserService.isLoggedIn() && path.indexOf('login') === -1) {
$location.path('/login/signin');
}
});
$urlRouterProvider.otherwise('/error/not-found');
}
angular.module('myApp', ['ngStorage']).config([
'$stateProvider',
function(
$stateProvider
) {
$stateProvider
.state('home', {...}) //not authed
.state('sign-up', {...}) //not authed
.state('login', {...}) //not authed
.state('authed', {...}) //authed, make all authed states children
.state('authed.dashboard', {...})
}])
.service('context', [
'$localStorage',
function(
$localStorage
) {
var _user = $localStorage.get('user');
return {
getUser: function() {
return _user;
},
authed: function() {
return (_user !== null);
},
// server should return some kind of token so the app
// can continue to load authenticated content without having to
// re-authenticate each time
login: function() {
return $http.post('/login.json').then(function(reply) {
if (reply.authenticated === true) {
$localStorage.set(_userKey, reply.user);
}
});
},
// this request should expire that token, rendering it useless
// for requests outside of this session
logout: function() {
return $http.post('logout.json').then(function(reply) {
if (reply.authenticated === true) {
$localStorage.set(_userKey, reply.user);
}
});
},
can: function(action, object) {
if (!this.authed()) {
return false;
}
var user = this.getUser();
if (user && user.type === 'admin') {
return true;
}
switch(action) {
case 'manage_dashboards':
return (user.type === 'manager');
}
return false;
}
}
}])
.controller('AuthCtrl', [
'context',
'$scope',
function(
context,
$scope
) {
$scope.$root.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
//only require auth if we're moving to another authed page
if (toState && toState.name.indexOf('authed') > -1) {
requireAuth();
}
});
function requireAuth() {
if (!context.authed()) {
$state.go('login');
}
}
}]
angular.module('myApp')
.factory('AuthService',
['$http', '$cookies', '$rootScope',
function ($http, $cookies, $rootScope) {
var service = {};
// Authenticates throug a rest service
service.authenticate = function (username, password, callback) {
$http.post('api/login', {username: username, password: password})
.success(function (response) {
callback(response);
});
};
// Creates a cookie and set the Authorization header
service.setCredentials = function (response) {
$rootScope.globals = response.token;
$http.defaults.headers.common['Authorization'] = 'Bearer ' + response.token;
$cookies.put('globals', $rootScope.globals);
};
// Checks if it's authenticated
service.isAuthenticated = function() {
return !($cookies.get('globals') === undefined);
};
// Clear credentials when logout
service.clearCredentials = function () {
$rootScope.globals = undefined;
$cookies.remove('globals');
$http.defaults.headers.common.Authorization = 'Bearer ';
};
return service;
}]);
angular.module('myApp', [
'ui.router',
'ngCookies'
])
.config(['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/resumen');
$stateProvider
.state("dashboard", {
url: "/dashboard",
templateUrl: "partials/dashboard.html",
controller: "dashCtrl",
data: {
authRequired: true
}
})
.state("login", {
url: "/login",
templateUrl: "partials/login.html",
controller: "loginController"
})
}])
.run(['$rootScope', '$transitions', '$state', '$cookies', '$http', 'AuthService',
function ($rootScope, $transitions, $state, $cookies, $http, AuthService) {
// keep user logged in after page refresh
$rootScope.globals = $cookies.get('globals') || {};
$http.defaults.headers.common['Authorization'] = 'Bearer ' + $rootScope.globals;
$transitions.onStart({
to: function (state) {
return state.data != null && state.data.authRequired === true;
}
}, function () {
if (!AuthService.isAuthenticated()) {
return $state.target("login");
}
});
}]);
data: {
authRequired: true
}
$transitions.onStart({
to: function (state) {
return state.data != null && state.data.authRequired === true;
}
}, function () {
if (!AuthService.isAuthenticated()) {
return $state.target("login");
}
});
$httpProvider.interceptors.push(function ($q) {
return {
'response': function (response) {
// TODO Create check for user authentication. With every request send "headers" or do some other check
return response;
},
'responseError': function (reject) {
// Forbidden
if(reject.status == 403) {
console.log('This page is forbidden.');
window.location = '/';
// Unauthorized
} else if(reject.status == 401) {
console.log("You're not authorized to view this page.");
window.location = '/';
}
return $q.reject(reject);
}
};
});