我正在开发一个Angular应用程序。在此,我在进入仪表板之前对用户进行身份验证。为实现这一目标,我将signIn function
写为
登录功能
this.signIn = function(credentials) {
console.info('AccountController[signIn] Called');
AuthService
.login(credentials)
.then(function(authenticatedUser) {
$scope.globals['currentUser'] = authenticatedUser;
AuthService.setCurrentUser(authenticatedUser);
$scope.globals['isAuthenticated'] = true;
$location.path('/dashboard');
}).catch(function(error) {
console.warn('AccountController[signIn] :: ', error);
Flash.Error(error);
$scope.credentials.password = '';
});
};
我还想限制用户访问路由,如果他们没有登录。为了达到这个目的,我想出了这个dirty code
。
路线
$stateProvider
.state('signIn', {
url: '/signIn',
templateUrl: 'partials/signIn/signIn.html',
data: {
pageTitle: 'SignIn'
},
controller: 'AccountController',
controllerAs: 'ac',
resolve: {
auth: ['$q', 'AuthService', function($q, AuthService) {
var userInfo = AuthService.isAuthenticated();
console.info('SignIn Route[isAuthenticated] :: ', userInfo);
if (!userInfo) {
return $q.when(userInfo);
} else {
return $q.reject({
isAuthenticated: true
});
}
}]
}
})
.state('dashboard', {
url: '/dashboard',
templateUrl: 'partials/dashboard.html',
controller: 'DashboardController',
access: {
requiredLogin: true
},
resolve: {
auth: ['$q', 'AuthService', function($q, AuthService) {
var authenticated = AuthService.isAuthenticated();
console.info('dashboard Route[isAuthenticated] :: ', authenticated);
if (authenticated) {
return $q.when(authenticated);
} else {
return $q.reject({
isAuthenticated: false
});
}
}]
}
})
.state('manageStudent', {
url: '/manageStudent',
templateUrl: 'partials/manageStudent.html',
access: {
requiredLogin: true
},
resolve: {
auth: ['$q', 'AuthService', function($q, AuthService) {
var authenticated = AuthService.isAuthenticated();
if (authenticated) {
return $q.when(authenticated);
} else {
return $q.reject({
isAuthenticated: false
});
}
}]
}
});
App.run(['$rootScope', 'settings', '$state', 'AuthService', '$location', function($rootScope, settings, $state, AuthService, $location) {
$rootScope.$state = $state; // state to be accessed from view
$rootScope.$settings = settings; // state to be accessed from view
$rootScope.$on('$stateChangeStart', function(event, next,nextParams,prev,prevParams) {
// If the user is logged in don't allow him to land on the Login Page
if (next.access !== undefined) {
if (next.access.requiredLogin && !AuthService.isAuthenticated()) {
$location.path('/signIn');
}
}
});
$rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) {
event.preventDefault();
if (!error.isAuthenticated) {
console.warn("I'm not Authenticated.Going to Sign-in");
return $location.path('/signIn');
} else {
console.info("I'm Authenticated");
$location.path('/dashboard');
}
});
}]);
原因我说上面的代码 DIRTY 是因为,如果我有10条路由我想要保护未经验证的用户,我必须在所有路由中复制相同的解析功能。
所以我的问题是,我该怎样做才能摆脱多个解析函数并能够编写DRY代码?
答案 0 :(得分:3)
由于auth
应该在每次路由更改时得到解决,因此将其包装到单独的factory
(这是一个单独的并且只运行一次)是不够的。为了解决这个限制,它应该是一个函数
app.factory('authResolver', function ($q, AuthService) {
return function () {
// ...
};
});
在每个路线上运行解析
...
resolve: {
auth: function (authResolver) {
return authResolver();
}
}
仍然不是干,但这是建议的湿度水平。
更加激进的方法可以保存样板resolve
并保存几行代码to that:
app.run(function ($rootScope, authResolver) {
$rootScope.$on('$stateChangeStart', function (e, to) {
if (to.doAuthPlease)
to.resolve.auth = authResolver();
});
});
和
...
doAuthPlease: true,
resolve: {}
在上述答案中与ngRoute的明显区别在于,在UI路由器中,您需要定义resolve
对象,以便能够动态地向状态添加新的解析器。它可以被视为like that或按原样保留。
答案 1 :(得分:0)
到目前为止,你走在正确的轨道上。您在状态对象上看起来像自定义数据成员access: { requiredLogin: true}
。
下一步是将其与ui-router
提供的State Change Events一起使用:
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState) {
if (toState.access.requiredLogin) {
if(!AuthService.isAuthenticated()) {
event.preventDefault();
// redirect to signIn?
}
}
});
这将放在你的.run
区块中,这意味着AuthService
也需要注入。这应该不需要在每个路由上使用resolve块。
希望有所帮助。
<强>更新强>
如果您的AuthService.isAuthenticated()
函数返回一个promise,依赖promise在事件处理程序中解析可能会有危险(它可能会在promise解析之前继续)。最好在块之前运行AuthService
函数(当应用程序启动时),然后将其存储在变量中:
var isAuth;
AuthService.isAuthenticated().then(function (result) { isAuth = result });
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState) {
if (toState.access.requiredLogin) {
if(!isAuth) {
event.preventDefault();
// redirect to signIn?
}
}
});
答案 2 :(得分:0)
var $delegate = $stateProvider.state;
$stateProvider.state = function(name, definition) {
var unrestricted = ['signIn'];
if (unrestricted.indexOf(name) === -1) {
definition.resolve = angular.extend({}, definition.resolve, {
auth: ['$q', 'AuthService', function($q, AuthService) {
var authenticated = AuthService.isAuthenticated();
if (authenticated) {
return $q.when(authenticated);
} else {
return $q.reject({
isAuthenticated: false
});
}
}]
});
}
return $delegate.apply(this, arguments);
};
我在这里动态地将解析添加到我想要限制的路线。
答案 3 :(得分:0)
因为您正在使用 ui.router状态(假设您正在使用v0.2.0或更高版本),您可以使用状态继承来解决此问题{{1并且不必在各种状态下复制它。
儿童国家从父母国家继承什么?
子状态 DO 从父状态继承以下内容:
- 通过解决方案解决了依赖关系
- 自定义数据属性
没有其他任何东西被继承(没有控制器,模板,网址等)。
继承已解决的依赖项
版本0.2.0中的新功能
子状态将从父状态继承已解析的依赖关系,它们可以覆盖它们。然后,您可以将已解析的依赖项注入控制器并解析子状态的功能。
我通过使用抽象基本状态来实现这一点,该基本状态将基本上定义您正在执行的操作,检查是否允许用户继续操作。由于我的所有UI状态都从抽象父状态继承,因此将为每个状态解析身份验证依赖关系。
抽象基础状态
resolve
其他州
.state('baseState', {
url: '',
abstract: true,
template: '<ui-view></ui-view>'
resolve: {
auth: ['$q', 'AuthService', function($q, AuthService) {
var authenticated = AuthService.isAuthenticated();
console.info('dashboard Route[isAuthenticated] :: ', authenticated);
if (authenticated) {
return $q.when(authenticated);
} else {
return $q.reject({
isAuthenticated: false
});
}
}]
}
})