Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Angular中的安全性验证 #23

Open
mmmaming opened this issue Aug 3, 2016 · 4 comments
Open

Angular中的安全性验证 #23

mmmaming opened this issue Aug 3, 2016 · 4 comments

Comments

@mmmaming
Copy link

mmmaming commented Aug 3, 2016

在开发的过程中,遇到身份验证的问题,如果在不登录的情况下直接访问项目的地址,怎么对用户的身份进行验证?常见的处理是:如果没有读取到用户的登录信息,则页面自动转向登录页面,那么作为开发人员,怎么实现此需求?目前据我所知的解决方案有两种。第一种是用ui-router提供的$stateChangeStart对于发生路由转换时进行判断,第二种则是用angular自带的拦截器进行处理。
先说第一种:$stateChangeStart
由于ui-router关心的是状态,所以我们可以在每次模板引擎被解析前触发此方法

 // 路由改变时判断是否登录,未登录则跳转至登录页面
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
        // 这里用event.preventDefault()可以阻止模板解析
        if (toState.name !== 'login' || fromState.name === 'login') {
             // 这里做身份验证的判断
            //  如判断cookie,session等。
           //  若判断不通过,则跳转至login页面
        }
 });

其中toState和fromState参数是获取路由的前一个状态和下一个状态,当发生路由状态改变时,如果访问的不是登陆页面或者不是从登陆页面跳转时,执行身份判断。
但是这种方法是有缺陷的,1.用到了$rootScope. 2.基于ui-router.3.不稳定(项目中测试发现).由于问题的存在,所以可以采用第二种拦截器的方法解决这些问题。

拦截器 Angular Interceptor
在与后台交互的过程中,有时会希望俘获一些请求,在其发送到服务端之前进行操作,或者在服务器完成响应执行调用前处理,拦截器就是为此应运而生的一种方法。
$httpProvider 中有一个 interceptors 数组,而所谓拦截器只是一个简单的注册到了该数组中的常规服务工厂。拦截器提供了四个方法:request , requestError ,response 和responseError 来对请求和响应进行处理。具体介绍看这里:
要实现身份验证,可以在每次$http请求到后台之前进行验证

function requestInterceptor($cookies,) {
    var requestConfig = {
        request: function(config) {
             // 这里做身份验证的判断
            //  如判断cookie,session等。
           //  若判断不通过,则跳转至login页面
            return config;
        }
    };
        return requestConfig;
}

该方法接收请求配置对象作为参数,然后必须返回配置对象或者 promise 。如果返回无效的配置对象或者 promise 则会被拒绝,导致 $http 调用失败。然后只需将创建的拦截器注册到$httpProvider的interceptors数组中即可。

module.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('myInterceptor');
}]);

在每次发送请求前,拦截器都会执行我们事先写好的判断代码,去做身份的验证。比stateChangeStart好的是,它在每次请求前都进行验证,而不只是对于路由切换时才验证。

拦截器可以做的事情还有很多,如请求恢复、loading等,搭配request , requestError ,response 和responseError实现即可,这里不做赘述。

@fnjoe
Copy link

fnjoe commented Aug 3, 2016

项目中,两者都用还是选择一种?会不会逻辑重复

@mmmaming
Copy link
Author

mmmaming commented Aug 3, 2016

@fnjoe 第二种的好处是,每次发起任何请求都会进行验证,这样避免了一种情况就是,当身份过期时,在当前页面发出请求不会被验证。如果用第一种就会有这种风险。

@fnjoe
Copy link

fnjoe commented Aug 3, 2016

@mmmaming
可以这样理解不,服务器将身份标识储存在session或者客户端的cookie里,每次请求都会携带cookie,然后在服务器端进行身份验证,前端使用http拦截器对相应的状态码做出反馈。

ng原生中有$locationChangeStart等监测url变化的事件,ngRoute中也有$routeChangeStart的事件,都是向下广播的,所有的子节点都可以注册监听函数。刚试了下,$stateChangeStart也是可以直接在子作用域中使用的。
不知道注册在$rootScope上是不是可以在第一时间进行权限的验证,从而选择是否阻止模板解析。

路由的权限验证可以考虑放置在路由的resolve中来进行,这样每个路由都拥有单独的安全验证。似乎和你们项目里统一处理路由验证的思路是反的,-_-,想想之后ng项目的集成时,说不定会用到。
不过无论ui-router还是ngRoute,所有基于路由的设计都会有resolve属性来进行路有成功前的处理。

@hjzheng
Copy link
Member

hjzheng commented Aug 18, 2016

补充一下,关于 AngularJS 拦截器一些有意思的用法

  1. 在request header中加入认证信息
  2. 处理服务器端异常状态
(function() {
    angular.module('app').config(config);
    config.$inject = ['$httpProvider', 'signProvider'];
    function config($httpProvider, signProvider) {
        $httpProvider.interceptors.push(['$rootScope', function($rootScope) {
            return {
                'request': function(config) {
                    var signs = signProvider.getSigns(config.url);
                    config.headers['access_token'] = signs.token;
                    config.headers['access_timestamp'] = signs.time;
                    return config;
                },
                'responseError': function(rejection) {
                    switch (rejection.status) {
                    case 401:
                        if (rejection.config.url !== 'api/login')
                            // If we're not on the login page
                            $rootScope.$broadcast('auth:loginRequired');
                        break;
                    case 403:
                        $rootScope.$broadcast('auth:forbidden');
                        break;
                    case 404:
                        $rootScope.$broadcast('page:notFound');
                        break;
                    case 500:
                        $rootScope.$broadcast('server:error');
                        break;
                    }
                    return rejection;
                }
            };
        }
        ]);
    }
})();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants