I'm trying to implement a user auth check and access check system in my app, however I keep hitting roadblocks. I think I have it the correct way this time but I have one last hurdle.
我正在尝试在我的应用程序中实现用户身份验证和访问检查系统,但是我一直在遇到障碍。我想这次我有正确的方法,但我有最后一个障碍。
A little background: I tried putting all of the code into the $rootScope.on($startChangeStart) and it worked, horribly... but it worked. The route was always redirected but due to the auth check on the backend it displayed the first request page for 1/2 a second and then the redirect page every time. Thus I tried 'pausing' page load by calling evt.preventDefault() right at the start of the $startChangeStart function, which worked, but trying to put the user back to the original route afterwards caused an infinite loop in the router.
一点背景:我尝试将所有代码放入$ rootScope.on($ startChangeStart)并且它工作得非常糟糕......但它确实有效。路由总是被重定向,但由于后端的auth检查,它显示第一个请求页面1/2秒,然后每次重定向页面。因此,我尝试通过在$ startChangeStart函数的开头调用evt.preventDefault()来“暂停”页面加载,这有效,但是尝试将用户放回到原始路由之后导致路由器中的无限循环。
So after more research and reading a lot of stack posts I'm certain that 'resolve:' is the proper place to put the auth check to ensure the page is not loading while it occurs, and then redirect the user if needed from the $startChangeStart. ($state and event are always undefined in my attempts to inject them into a resolve function) It seems like the winning combination.
因此,经过更多的研究和阅读大量的帖子后,我确信'resolve:'是进行auth检查的正确位置,以确保页面在发生时不加载,然后根据需要从$重定向用户startChangeStart。 ($状态和事件在我尝试将它们注入解析函数时总是未定义的)这似乎是获胜的组合。
My problem: I have the resolve on the root state in my app: 'main'
我的问题:我对我的应用中的root状态有了决心:'main'
This was to avoid code redundancy, however I cannot determine how to access the root state's properties, and therefore the resolve result, from the $stateChangeStart function. The toState is the child state, while the fromState is either the previous state or an abstract state with the '^' route...
这是为了避免代码冗余,但我无法确定如何从$ stateChangeStart函数访问根状态的属性,从而确定解析结果。 toState是子状态,而fromState是先前的状态或具有'^'路由的抽象状态...
Do I have to put the resolve on every child state for this to work, or is there a way to access the root state from this point?
我是否必须将解决方案放在每个子状态才能使用,或者是否有办法从此处访问根状态?
Basic app setup:
基本应用设置:
angular.module('App', ['ui.router', 'ui.bootstrap', 'ui.event', 'AngularGM', 'ngResource'])
.config(['$urlRouterProvider', '$stateProvider', function($urlRouterProvider, $stateProvider){
$urlRouterProvider
.when('/home', '/')
.when('', '/')
.when('/sign-up/joe', '/sign-up')
.otherwise('/');
$stateProvider
.state('main', {
url: '',
abstract: true,
templateUrl: 'views/main.html',
controller: 'MainCtrl',
resolve: {
checkAccess: ['accountService', function(accountService) {
accountService.checkAuth(function(){
accountService.checkAccess(function (access){
return access;
});
});
}]
}
})
.state('main.home', {
url: '',
abstract: true,
templateUrl: 'views/home.html',
controller: 'HomeCtrl'
})
.state('main.home.index', {
url: '/',
templateUrl: 'views/home/index.html'
});
.run(['$rootScope', '$state', '$stateParams', 'accountService', function ($rootScope, $state, $stateParams) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
console.dir(toState);
console.dir(toParams);
console.dir(fromState);
console.dir(fromParams);
if (!toState.checkAccess.allowed) {
event.preventDefault();
$state.transitionTo(toState.checkAccess.newState);
}
});
}]);
This is the output from the console.dir() calls on the two state objects:
这是对两个状态对象的console.dir()调用的输出:
Object
name: "main.home.index"
templateUrl: "views/home/index.html"
url: "/"
__proto__: Object
Object
controller: "PlacesCtrl"
name: "main.places.search"
templateUrl: "views/places.html"
url: "/places"
__proto__: Object
Update
更新
Oops, forgot to mention AngularJS version is v1.2.0-rc.2
哎呀,忘了提一下AngularJS版本是v1.2.0-rc.2
$state.current console.dir()
$ state.current console.dir()
Object
abstract: true
name: ""
url: "^"
views: null
__proto__: Object
5 个解决方案
#1
8
Yes, I believe you can access root state from the $stateChangeStart
function.
是的,我相信你可以从$ stateChangeStart函数访问root状态。
When using pure AngularJS I normally use current.$$route
当使用纯AngularJS时,我通常使用当前的。$$路由
For example, using the following route
例如,使用以下路线
.when('/home', {
title:'Home',
bodyClass: 'meetings',
controler: 'HomeCtrl'
})
I can access the root state like so
我可以像这样访问root状态
$rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
if (current.$$route) {
$rootScope.title = current.$$route.title;
$rootScope.bodyClass = current.$$route.bodyClass;
}
});
Using ui-router
it's just a bit different as it's called $state.current
. And you can access all the properties associated to whatever route you hit (e.g: $state.current.url)
使用ui-router它只是有点不同,因为它被称为$ state.current。并且您可以访问与您命中的任何路径相关联的所有属性(例如:$ state.current.url)
So on your code you could have something like this
所以在你的代码上你可以得到类似的东西
.run(['$rootScope', '$state', '$stateParams', function ($rootScope, $state, $stateParams) {
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
console.log($state.current.url);
});
}]);
#2
1
You need not to use resolve
. Take a look at my solution:
你不需要使用解决方案。看看我的解决方案:
app.run ($rootScope, $state, Auth, ngDialog) ->
$rootScope.$on '$stateChangeStart', (e, to) ->
if to.authRequired and not Auth.isAuthenticated()
e.preventDefault()
Auth.currentUser().then(
(user) ->
$state.go(to)
(failure) ->
$rootScope.storedState = to
$state.go('main')
ngDialog.closeAll()
ngDialog.open
template: 'modals/login.html'
controller: 'loginCtrl'
className: 'ngdialog-theme-default'
)
I use angular_devise and ngDialog but they are optional and you can implement it with your own user's service.
我使用angular_devise和ngDialog但它们是可选的,您可以使用自己的用户服务来实现它。
#3
0
Is it possible to do the redirect from within your accountService
? If you detect that the user fails your checkAuth or checkAccess functions, you could prevent the callback from executing and redirect the user to your error (or login) page.
是否可以从您的accountService中进行重定向?如果检测到用户未通过checkAuth或checkAccess函数,则可以阻止回调执行并将用户重定向到错误(或登录)页面。
Something else to consider is implementing some sort of variable/queue of states if you'd like to redirect someone to the login page to refresh their authorization/authentication and then return to the previous state.
如果您想将某人重定向到登录页面以刷新其授权/身份验证,然后返回到先前的状态,则需要考虑的其他事项是实现某种状态的变量/队列。
#4
0
If you initialize your state with a default, empty object on resolve, you'll be able to manipulate it within $stateChangeStart
.
如果在解析时使用默认的空对象初始化状态,则可以在$ stateChangeStart中对其进行操作。
$stateProvider
.state 'home',
url: "/"
resolve: {}
...
$rootScope.$on '$stateChangeStart', (e, toState, toParams, fromState, fromParams) ->
toState.resolve.x = ->
$timeout ->
alert "done"
, 3000
See https://github.com/angular-ui/ui-router/issues/1165
请参阅https://github.com/angular-ui/ui-router/issues/1165
#5
0
This answer is very late but it can be useful.
这个答案很晚,但它可能很有用。
Resolves of a state and $stateChangeStart
event are executed at the same time. By the time you try to access resolved data in $stateChangeStart
, it'll not be available but it'll be available when $stateChangeSuccess
event fires.
解析状态和$ stateChangeStart事件同时执行。当您尝试访问$ stateChangeStart中的已解析数据时,它将无法使用,但在$ stateChangeSuccess事件触发时它将可用。
If you use $stateChangeStart
then you'll need to do checkAuth
from two places $stateChangeStart
event and main resolve. Since they have parallel execution, at least 2 network requests will be sent to server for the same data.
如果你使用$ stateChangeStart,那么你需要从两个地方进行checkAuth $ stateChangeStart事件和主要解析。由于它们具有并行执行功能,因此将向服务器发送至少2个网络请求以获取相同的数据。
Instead use $stateChangeSuccess
. Using this will ensure that your resolves are resolved and you can then check access. Also, instead of accessing resolved properties,access resolved data using angular service.
而是使用$ stateChangeSuccess。使用此选项可确保您的结算已解决,然后您可以检查访问权限。此外,不使用已解析的属性,而是使用角度服务访问已解析的数据。
#1
8
Yes, I believe you can access root state from the $stateChangeStart
function.
是的,我相信你可以从$ stateChangeStart函数访问root状态。
When using pure AngularJS I normally use current.$$route
当使用纯AngularJS时,我通常使用当前的。$$路由
For example, using the following route
例如,使用以下路线
.when('/home', {
title:'Home',
bodyClass: 'meetings',
controler: 'HomeCtrl'
})
I can access the root state like so
我可以像这样访问root状态
$rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
if (current.$$route) {
$rootScope.title = current.$$route.title;
$rootScope.bodyClass = current.$$route.bodyClass;
}
});
Using ui-router
it's just a bit different as it's called $state.current
. And you can access all the properties associated to whatever route you hit (e.g: $state.current.url)
使用ui-router它只是有点不同,因为它被称为$ state.current。并且您可以访问与您命中的任何路径相关联的所有属性(例如:$ state.current.url)
So on your code you could have something like this
所以在你的代码上你可以得到类似的东西
.run(['$rootScope', '$state', '$stateParams', function ($rootScope, $state, $stateParams) {
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
console.log($state.current.url);
});
}]);
#2
1
You need not to use resolve
. Take a look at my solution:
你不需要使用解决方案。看看我的解决方案:
app.run ($rootScope, $state, Auth, ngDialog) ->
$rootScope.$on '$stateChangeStart', (e, to) ->
if to.authRequired and not Auth.isAuthenticated()
e.preventDefault()
Auth.currentUser().then(
(user) ->
$state.go(to)
(failure) ->
$rootScope.storedState = to
$state.go('main')
ngDialog.closeAll()
ngDialog.open
template: 'modals/login.html'
controller: 'loginCtrl'
className: 'ngdialog-theme-default'
)
I use angular_devise and ngDialog but they are optional and you can implement it with your own user's service.
我使用angular_devise和ngDialog但它们是可选的,您可以使用自己的用户服务来实现它。
#3
0
Is it possible to do the redirect from within your accountService
? If you detect that the user fails your checkAuth or checkAccess functions, you could prevent the callback from executing and redirect the user to your error (or login) page.
是否可以从您的accountService中进行重定向?如果检测到用户未通过checkAuth或checkAccess函数,则可以阻止回调执行并将用户重定向到错误(或登录)页面。
Something else to consider is implementing some sort of variable/queue of states if you'd like to redirect someone to the login page to refresh their authorization/authentication and then return to the previous state.
如果您想将某人重定向到登录页面以刷新其授权/身份验证,然后返回到先前的状态,则需要考虑的其他事项是实现某种状态的变量/队列。
#4
0
If you initialize your state with a default, empty object on resolve, you'll be able to manipulate it within $stateChangeStart
.
如果在解析时使用默认的空对象初始化状态,则可以在$ stateChangeStart中对其进行操作。
$stateProvider
.state 'home',
url: "/"
resolve: {}
...
$rootScope.$on '$stateChangeStart', (e, toState, toParams, fromState, fromParams) ->
toState.resolve.x = ->
$timeout ->
alert "done"
, 3000
See https://github.com/angular-ui/ui-router/issues/1165
请参阅https://github.com/angular-ui/ui-router/issues/1165
#5
0
This answer is very late but it can be useful.
这个答案很晚,但它可能很有用。
Resolves of a state and $stateChangeStart
event are executed at the same time. By the time you try to access resolved data in $stateChangeStart
, it'll not be available but it'll be available when $stateChangeSuccess
event fires.
解析状态和$ stateChangeStart事件同时执行。当您尝试访问$ stateChangeStart中的已解析数据时,它将无法使用,但在$ stateChangeSuccess事件触发时它将可用。
If you use $stateChangeStart
then you'll need to do checkAuth
from two places $stateChangeStart
event and main resolve. Since they have parallel execution, at least 2 network requests will be sent to server for the same data.
如果你使用$ stateChangeStart,那么你需要从两个地方进行checkAuth $ stateChangeStart事件和主要解析。由于它们具有并行执行功能,因此将向服务器发送至少2个网络请求以获取相同的数据。
Instead use $stateChangeSuccess
. Using this will ensure that your resolves are resolved and you can then check access. Also, instead of accessing resolved properties,access resolved data using angular service.
而是使用$ stateChangeSuccess。使用此选项可确保您的结算已解决,然后您可以检查访问权限。此外,不使用已解析的属性,而是使用角度服务访问已解析的数据。