簡體   English   中英

角度認證:避免針對不同的路由進行多次解析

[英]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版中的新功能

子狀態將從父狀態繼承已解析的依賴項,並可以將其覆蓋。 然后,您可以將已解決的依賴項注入到控制器中並解決子狀態的功能。

src- https://github.com/angular-ui/ui-router/wiki/Nested-States-%26-Nested-Views#what-do-child-states-inherit-from-parent-states

我通過使用抽象的基本狀態來實現此目的,該基本狀態將定義您正在做的事情,檢查用戶是否被允許繼續。 由於我的所有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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM