[英]Angular Authentication : Avoid multiple resolve for different routes
我正在開發一個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條要防止未經身份驗證的用戶訪問的路由,則必須在所有路由中復制相同的resolve函數。
所以我的問題是,我應該怎么做才能擺脫多重解析功能並能夠編寫DRY代碼?
由於auth
應該在每次路由更改時都得到解決,因此僅將其包裝到單獨的factory
(這是一個單例並且僅運行一次)是不夠的。 為了解決這個限制,它應該是一個函數
app.factory('authResolver', function ($q, AuthService) {
return function () {
// ...
};
});
在每條路線上運行
...
resolve: {
auth: function (authResolver) {
return authResolver();
}
}
仍然不是那種干燥,但這是推薦的濕度水平。
可能從樣板resolve
節省一種方法並節省幾行代碼的更激進的方法將類似於以下內容 :
app.run(function ($rootScope, authResolver) {
$rootScope.$on('$stateChangeStart', function (e, to) {
if (to.doAuthPlease)
to.resolve.auth = authResolver();
});
});
和
...
doAuthPlease: true,
resolve: {}
提到的答案與ngRoute的明顯區別在於,在UI Router中,您需要定義resolve
對象,以便能夠動態將新的resolver添加到狀態中。 可以像這樣對待它,也可以照原樣進行。
到目前為止,您在正確的軌道上。 您在狀態對象上看起來像是自定義數據成員access: { requiredLogin: true}
。
下一步是將它與ui-router
提供的狀態更改事件一起使用:
$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()
函數返回了一個AuthService.isAuthenticated()
,則依靠AuthService.isAuthenticated()
在事件處理程序中解析可能有潛在的危險(它可能會在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?
}
}
});
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);
};
在這里,我將解析度動態添加到要限制的路由中。
因為您使用的是ui.router狀態 (並假設您使用的是v0.2.0或更高版本),所以您可以使用狀態繼承通過resolve
解決此resolve
,而不必在各種狀態下都進行復制。
兒童國家從父國家繼承什么?
子州確實要從父州繼承以下內容:
- 通過resolve解決依賴關系
- 自定義數據屬性
沒有其他繼承(沒有控制器,模板,URL等)。
繼承的已解決依賴項
0.2.0版中的新功能
子狀態將從父狀態繼承已解析的依賴項,並可以將其覆蓋。 然后,您可以將已解決的依賴項注入到控制器中並解決子狀態的功能。
我通過使用抽象的基本狀態來實現此目的,該基本狀態將定義您正在做的事情,檢查用戶是否被允許繼續。 由於我的所有UI狀態都繼承自抽象父狀態,因此將為每個狀態解析身份驗證依賴性。
抽象基本狀態
.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
});
}
}]
}
})
其他州
.state('dashboard', {
parent: 'baseState'
url: '/dashboard',
templateUrl: 'partials/dashboard.html',
controller: 'DashboardController',
...
})
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.