一尘不染

角度认证:避免针对不同的路由进行多次解析

angularjs

我正在开发一个Angular应用程序。在此,我将在进入仪表板之前对用户进行身份验证。为了达到这个目的,我写了signIn functionas

登录功能

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 = '';
            });
    };

我还想限制用户访问路由(如果尚未登录)。为此,我想到了this 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代码?


阅读 207

收藏
2020-07-04

共1个答案

一尘不染

由于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的明显区别是,在UIRouter中,您需要resolve定义对象才能动态地向状态添加新的解析器。可以像这样对待它,也可以照原样进行。

2020-07-04