繁体   English   中英

使用有角度的前端处理身份验证->如何保护特定页面?

[英]Handling authentication with angular front-end -> How to protect specific pages?

希望在Node.js中构建具有用户登录(身份验证)功能的Web应用程序,该应用程序具有3个非安全页面(/ home,/ contact,/ about)和一个安全页面(/ admin)。 顺便说一句,我一直在参考scotch.io Mean Machine书。

我遇到的问题是我已经将所有内容都构建好了,登录机制的工作原理是:登录时,我被定向到/ admin; 但是,当我不登录而进入URL中的/ admin时,仍可以访问该页面。 也就是说,我不确定将实际保护放在何处。

下面是我如何布置应用程序的信息。 希望有一个概念上的答案来建议我应该如何做,而不是仅是代码答案。

服务:

  • auth服务将输入的用户名/密码发布到服务器,并返回false或成功(带有用户信息和JWT令牌)
  • auth服务还将令牌(如果有)作为AuthInterceptor注入到每个HTTP标头中

路由器:

angular.module('routerRoutes', ['ngRoute'])
    .config(function($routeProvider, $locationProvider) {
        $routeProvider
            .when('/', {
                templateUrl:    'views/home.html',
                controller:     'homeController',
                controllerAs:   'home'
            })
            .when('/about', {
                templateUrl:    'views/about.html',
                controller:     'aboutController',
                controllerAs:   'about'
            })
            .when('/contact', {
                templateUrl:    'views/contact.html',
                controller:     'contactController',
                controllerAs:   'contact'
            })
            .when('/login', {
                templateUrl:    'views/login.html',
                controller:     'adminController',
                controllerAs:   'login'
            })
            .when('/admin', {
                templateUrl:    'views/admin/admin.html',
                controller:     'adminController',
                controllerAs:   'admin'
            });

        $locationProvider.html5Mode(true);

    });

控制器:

  • homeController,aboutController和contactController通常现在为空

  • adminController:

    .controller('adminController',function($ rootScope,$ location,Auth){

     var vm = this; vm.loggedIn = Auth.isLoggedIn(); $rootScope.$on('$routeChangeStart', function() { vm.loggedIn = Auth.isLoggedIn(); window.alert(vm.loggedIn); // this gives correct answer and works Auth.getUser() .success(function(data) { vm.user = data; }); }); vm.doLogin = function() { vm.error = ''; Auth.login(vm.loginData.username, vm.loginData.password) .success(function(data) { vm.user = data.username; if (data.success) $location.path('/admin'); else vm.error = data.message; }); }; vm.doLogout = function() { Auth.logout(); vm.user = {}; $location.path('/login'); }; 

    });

最后,下面是我的index.html(仅是正文):

<body class="container" ng-app="meanApp" ng-controller="adminController as admin">

    <a href="/"><i class="fa fa-home">Home </i></a>
    <a href="/about"><i class="fa fa-shield">About </i></a>
    <a href="/contact"><i class="fa fa-comment">Contact</i></a>
    <a href="/admin"><i class="fa fa-comment">Admin</i></a>

    <ul class="nav navbar-nav navbar-right">
        <li ng-if="!admin.loggedIn"><a href="/login">Login</a></li>
        <li ng-if="admin.loggedIn" class="navbar-text">Hello {{ admin.user.username }}</li>
        <li ng-if="admin.loggedIn"><a href="#" ng-click="admin.doLogout()">Logout</a></li>
    </ul>

    <main>
        <div ng-view>

        </div>
    </main>

</body>

我将不会粘贴注入的其他html页面,因为它们上还没有任何东西(login.html只有两个输入字段和提交按钮)。

有几个问题:

  • 在我的index.html中,当我单击/ admin时,即使我没有登录,它也带我进入管理页面。我应该在哪里防止这种情况发生?
  • 对我未遵循的设置和最佳做法有任何一般性评论吗?

另一个优点:

  • 我读到,如果未命中决策树的该分支,则不会在“查看源代码”中显示“ li ng-if =“,但确实如此。 我被误导了还是做错了什么?

我采用了自定义属性路由来保护应用程序中的路由。 侦听并检查每个发生的状态更改是否具有此属性。 如果设置了此属性,则它将检查用户是否已登录;如果未登录,则会将其路由到“登录”状态。

我在实现该功能的当前项目中使用了UI-ROUTER。 我在路由中使用了一个自定义参数“ data”。

在.config块中声明我的开放路线:

      $stateProvider
         .state('login', {
             url: '/login',
             templateUrl: 'login/login.html',
             controller: 'LoginController',
             controllerAs: 'vm'
          })
         .state('home', {
             url: '',
             templateUrl: 'layout/shell.html',
             controller: 'ShellController',
             controllerAs: 'vm',
             data: {
                 requireLogin: true
             }
          })

然后,将其添加到应用程序中的.run,在其中查找ui路由器的$ stateChangeStart事件并在状态声明中查看我的自定义属性(“数据”):

     $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {

        var requireLogin = toState.hasOwnProperty('data') && toState.data.requireLogin;
        if (requireLogin && !authService.isLoggedIn()) {
            event.preventDefault();
            authService.setDestinationState(toState.name);
            $state.go('login');
        }

        if (toState.name !== 'login') {
            authService.setDestinationState(toState.name);
        }
    });

如果您想知道authService.setDestinationState的功能是什么...它将保留用户尝试访问的URL ...一旦成功登录,它将自动将其转发到该状态(请参见下文):

     function login() {
        authService.authLogin(vm.credentials)
            .then(loginComplete)
            .catch(loginFailed);

        function loginComplete(data, status, headers, config) {
            vm.user = data;
            $rootScope.$broadcast('authorized');
            $state.go(authService.getDestinationState());
        }

        function loginFailed(status) {
            console.log('XHR Failed for login.');
            vm.user = undefined;
            vm.error = 'Error: Invalid user or password. ' + status.error;
            toastr.error(vm.error, {closeButton: true} );
        }
    }

定义管理路由时,可以定义一个名为resolve的属性。 resolve中的每个属性都是一个函数(可以是可注入函数)。 这个函数应该返回一个promise,promise的结果可以注入到控制器中。

有关解决的更多信息,请参见http://odetocode.com/blogs/scott/archive/2014/05/20/using-resolve-in-angularjs-routes.aspx

您可以按以下方式使用resolve进行身份验证检查。

var authenticateRoute = ['$q', '$http' , function ($q, $http) {
    var deferred = $q.defer();
    $http.get("http://api.domain.com/whoami")
         .then(function(response) { 
                   if (response.data.userId) deferred.resolve(response.data.userId);  
                   else window.location.href = "#/Login"
               });
    return deferred.promise();
}]

// ...

.when('/admin', {
    templateUrl:    'views/admin/admin.html',
    controller:     'adminController',
    controllerAs:   'admin',
    resolve: {
        authenticatedAs: authenticateRoute
    }
});

这样,您可以传递经过身份验证的用户ID(即使为null),也可以让控制器处理它,例如,如果您需要上下文消息。 否则,您可以执行上述操作,并且仅在身份验证请求中有用户ID时才这样做,否则重定向到您的登录路由。

希望这可以帮助! / AJ

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM