AngularJS:如何使用$ resource请求发送身份验证令牌?

Nil*_*ste 62 javascript authentication angularjs

我想在从API请求资源时发送身份验证令牌.

我确实使用$ resource实现了一项服务:

factory('Todo', ['$resource', function($resource) {
 return $resource('http://localhost:port/todos.json', {port:":3001"} , {
   query: {method: 'GET', isArray: true}
 });
}])
Run Code Online (Sandbox Code Playgroud)

我有一个存储身份验证令牌的服务:

factory('TokenHandler', function() {
  var tokenHandler = {};
  var token = "none";

  tokenHandler.set = function( newToken ) {
    token = newToken;
  };
  tokenHandler.get = function() {
    return token;
  };

  return tokenHandler;
});
Run Code Online (Sandbox Code Playgroud)

我想tokenHandler.get通过Todo服务发送的每个请求发送令牌.我能够通过将其置于特定动作的调用中来发送它.例如,这有效:

Todo.query( {access_token : tokenHandler.get()} );
Run Code Online (Sandbox Code Playgroud)

但我更愿意将access_token定义为Todo服务中的参数,因为它必须随每次调用一起发送.并改善干旱.但是工厂中的所有东西只执行一次,因此在定义工厂之前必须提供access_token,之后它不能更改.

有没有办法在服务中放置动态更新的请求参数?

Nil*_*ste 60

感谢Andy Joslin.我选择了包装资源操作的想法.资源的服务现在看起来像这样:

.factory('Todo', ['$resource', 'TokenHandler', function($resource, tokenHandler) {
  var resource = $resource('http://localhost:port/todos/:id', {
    port:":3001",
    id:'@id'
    }, {
      update: {method: 'PUT'}
    });

  resource = tokenHandler.wrapActions( resource, ["query", "update"] );

  return resource;
}])
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,资源首先是通常的定义方式.在我的例子,这包括所谓的自定义操作update.之后,该tokenHandler.wrapAction()方法的返回会覆盖资源,该方法将资源和一系列操作作为参数.

正如您所期望的那样,后一种方法实际上将操作包装在每个请求中包含auth令牌并返回修改后的资源.那么让我们来看看代码:

.factory('TokenHandler', function() {
  var tokenHandler = {};
  var token = "none";

  tokenHandler.set = function( newToken ) {
    token = newToken;
  };

  tokenHandler.get = function() {
    return token;
  };

  // wrap given actions of a resource to send auth token with every
  // request
  tokenHandler.wrapActions = function( resource, actions ) {
    // copy original resource
    var wrappedResource = resource;
    for (var i=0; i < actions.length; i++) {
      tokenWrapper( wrappedResource, actions[i] );
    };
    // return modified copy of resource
    return wrappedResource;
  };

  // wraps resource action to send request with auth token
  var tokenWrapper = function( resource, action ) {
    // copy original action
    resource['_' + action]  = resource[action];
    // create new action wrapping the original and sending token
    resource[action] = function( data, success, error){
      return resource['_' + action](
        angular.extend({}, data || {}, {access_token: tokenHandler.get()}),
        success,
        error
      );
    };
  };

  return tokenHandler;
});
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,该wrapActions()方法从其参数创建资源的副本,并在actions数组中循环tokenWrapper()以为每个操作调用另一个函数.最后,它返回资源的修改副本.

tokenWrapper方法首先创建预先存在的资源动作的副本.此副本具有尾随下划线.因此query()变得_query().然后,新方法将覆盖原始query()方法._query()正如Andy Joslin所建议的那样,这个新方法包含了通过该动作发送的每个请求的auth令牌.

这种方法的好处是,我们仍然可以使用每个angularjs资源(获取,查询,保存等)附带的预定义操作,而无需重新定义它们.在其余代码中(例如在控制器内),我们可以使用默认操作名称.

  • 写了一篇关于它的博客文章.在这里查看:http://nils-blum-oeste.net/angularjs-send-auth-token-with-every--request (7认同)

Ben*_*ing 34

另一种方法是使用HTTP拦截器,用当前的OAuth令牌替换"魔术"授权头.下面的代码是针对OAuth的,但对于读者而言,这是一个简单的练习.

// Injects an HTTP interceptor that replaces a "Bearer" authorization header
// with the current Bearer token.
module.factory('oauthHttpInterceptor', function (OAuth) {
  return {
    request: function (config) {
      // This is just example logic, you could check the URL (for example)
      if (config.headers.Authorization === 'Bearer') {
        config.headers.Authorization = 'Bearer ' + btoa(OAuth.accessToken);
      }
      return config;
    }
  };
});

module.config(function ($httpProvider) {
  $httpProvider.interceptors.push('oauthHttpInterceptor');
});
Run Code Online (Sandbox Code Playgroud)


ric*_*cit 21

我真的很喜欢这种方法:

http://blog.brunoscopelliti.com/authentication-to-a-restful-web-service-in-an-angularjs-web-app

其中令牌始终在请求标头内自动发送,而不需要包装器.

// Define a new http header
$http.defaults.headers.common['auth-token'] = 'C3PO R2D2';
Run Code Online (Sandbox Code Playgroud)

  • 一个缺点是它将被发送给所有请求 - 如果您正在访问多个后端,这可能是一个问题.对于大多数基本情况,这将是合适的. (6认同)
  • @InancGumus因为那时您将您的凭证/令牌发送到不相关的服务 - MITM /重播攻击的风险. (3认同)

And*_*lin 9

您可以为它创建包装函数.

app.factory('Todo', function($resource, TokenHandler) {
    var res= $resource('http://localhost:port/todos.json', {
        port: ':3001',
    }, {
        _query: {method: 'GET', isArray: true}
    });

    res.query = function(data, success, error) {
        //We put a {} on the first parameter of extend so it won't edit data
        return res._query(
            angular.extend({}, data || {}, {access_token: TokenHandler.get()}),
            success,
            error
        );
    };

    return res;
})
Run Code Online (Sandbox Code Playgroud)


vpo*_*ain 5

我也必须处理这个问题.我不认为它是否是一个优雅的解决方案,但它的工作原理有两行代码:

我想您在SessionService中进行身份验证后从服务器获取令牌.然后,调用这种方法:

   angular.module('xxx.sessionService', ['ngResource']).
    factory('SessionService', function( $http,  $rootScope) {

         //...
       function setHttpProviderCommonHeaderToken(token){
          $http.defaults.headers.common['X-AUTH-TOKEN'] = token;
       }  
   });
Run Code Online (Sandbox Code Playgroud)

之后,来自$ resource和$ http的所有请求都会在其标题中包含令牌.