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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11176330/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 04:47:16  来源:igfitidea点击:

AngularJS: How to send auth token with $resource requests?

javascriptauthenticationangularjs

提问by Nils Blum-Oeste

I want to send an auth token when requesting a resource from my API.

我想在从我的 API 请求资源时发送一个身份验证令牌。

I did implement a service using $resource:

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

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

And I have a service that stores the auth token:

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

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

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

  return tokenHandler;
});

I would like to send the token from tokenHandler.getwith every request send via the Todoservice. I was able to send it by putting it into the call of a specific action. For example this works:

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

Todo.query( {access_token : tokenHandler.get()} );

But I would prefer to define the access_token as a parameter in the Todoservice, as it has to be sent with every call. And to improve DRY. But everything in the factory is executed only once, so the access_token would have to be available before defining the factory and it cant change afterwards.

但我更愿意将 access_token 定义为Todo服务中的参数,因为每次调用都必须发送它。并改善DRY。但是工厂中的所有内容都只执行一次,因此在定义工厂之前 access_token 必须可用,并且之后不能更改。

Is there a way to put a dynamically updated request parameter in the service?

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

采纳答案by Nils Blum-Oeste

Thanks to Andy Joslin. I picked his idea of wrapping the resource actions. The service for the resource looks like this now:

感谢安迪·乔斯林。我选择了他包装资源操作的想法。资源的服务现在看起来像这样:

.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;
}])

As you can see the resource is defined the usual way in the first place. In my example this includes a custom action called update. Afterwards the resource is overwritten by the return of the tokenHandler.wrapAction()method which takes the resource and an array of actions as parameters.

如您所见,资源首先以通常的方式定义。在我的示例中,这包括一个名为update. 之后,资源被tokenHandler.wrapAction()方法的返回覆盖,该方法将资源和操作数组作为参数。

As you would expect the latter method actually wraps the actions to include the auth token in every request and returns a modified resource. So let's have a look at the code for that:

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

.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;
});

As you can see the wrapActions()method creates a copy of the resource from it's parameters and loops through the actionsarray to call another function tokenWrapper()for every action. In the end it returns the modified copy of the resource.

如您所见,该wrapActions()方法根据其参数创建资源的副本,并循环遍历actions数组tokenWrapper()为每个操作调用另一个函数。最后它返回资源的修改副本。

The tokenWrappermethod first of all creates a copy of preexisting resource action. This copy has a trailing underscore. So query()becomes _query(). Afterwards a new method overwrites the original query()method. This new method wraps _query(), as suggested by Andy Joslin, to provide the auth token with every request send through that action.

tokenWrapper方法首先创建一个预先存在的资源操作的副本。此副本有一个尾随下划线。于是query()变成_query()。之后,新方法会覆盖原始query()方法。这个新方法 wraps _query(),正如安迪乔斯林所建议的那样,为通过该操作发送的每个请求提供身份验证令牌。

The good thing with this approach is, that we still can use the predefined actions which come with every angularjs resource (get, query, save, etc.), without having to redefine them. And in the rest of the code (within controllers for example) we can use the default action name.

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

回答by Ben Walding

Another way is to use an HTTP interceptor which replaces a "magic" Authorization header with the current OAuth token. The code below is OAuth specific, but remedying that is a simple exercise for the reader.

另一种方法是使用 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');
});

回答by ricricucit

I really like this approach:

我真的很喜欢这种方法:

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

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

where the token is always automagically sent within the request header without the need of a wrapper.

令牌始终在请求标头中自动发送,无需包装器。

// Define a new http header
$http.defaults.headers.common['auth-token'] = 'C3PO R2D2';

回答by Andrew Joslin

You could create a wrapper function for it.

您可以为它创建一个包装函数。

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

回答by vpoulain

I had to deal with this problem as well. I don't think if it is an elegant solution but it works and there are 2 lines of code :

我也不得不处理这个问题。我不认为这是否是一个优雅的解决方案,但它有效并且有两行代码:

I suppose you get your token from your server after an authentication in SessionService for instance. Then, call this kind of method :

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

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

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

After that all your requests from $resource and $http will have token in their header.

之后,您来自 $resource 和 $http 的所有请求都将在其标头中包含令牌。

回答by ganmor

Another solution would be to use resource.bind(additionalParamDefaults), that return a new instance of the resource bound with additional parameters

另一种解决方案是使用 resource.bind(additionalParamDefaults),它返回一个绑定了附加参数的资源的新实例

var myResource = $resource(url, {id: '@_id'});
var myResourceProtectedByToken = myResource.bind({ access_token : function(){
        return tokenHandler.get();
}});
return myResourceProtectedByToken;

The access_token function will be called every time any of the action on the resource is called.

每次调用资源上的任何操作时都会调用 access_token 函数。

回答by Miquel

Following your accepted answer, I would propose to extend the resource in order to set the token with the Todo object:

根据您接受的答案,我建议扩展资源以使用 Todo 对象设置令牌:

.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"] );
  resource.prototype.setToken = function setTodoToken(newToken) {
    tokenHandler.set(newToken);
  };
  return resource;
}]);

In that way there is no need to import the TokenHandler each time you want to use the Todo object and you can use:

这样,每次要使用 Todo 对象时都不需要导入 TokenHandler ,您可以使用:

todo.setToken(theNewToken);

todo.setToken(theNewToken);

Another change I would do is to allow default actions if they are empty in wrapActions:

我要做的另一个更改是允许默认操作,如果它们为空wrapActions

if (!actions || actions.length === 0) {
  actions = [];
  for (i in resource) {
    if (i !== 'bind') {
      actions.push(i);
    }
  }
}

回答by Darragh Enright

I might be misunderstanding all of your question (feel free to correct me :) ) but to specifically address adding the access_tokenfor every request, have you tried injecting the TokenHandlermodule into the Todomodule?

我可能误解了您的所有问题(请随时纠正我:))但要专门解决access_token为每个请求添加的问题,您是否尝试过将TokenHandler模块注入Todo模块?

// app
var app = angular.module('app', ['ngResource']);

// token handler
app.factory('TokenHandler', function() { /* ... */ });

// inject the TokenHandler
app.factory('Todo', function($resource, TokenHandler) {
    // get the token
    var token = TokenHandler.get();
    // and add it as a default param
    return $resource('http://localhost:port/todos.json', {
        port: ':3001',
        access_token : token
    });
})

You can call Todo.query()and it will append ?token=noneto your URL. Or if you prefer to add a token placeholder you can of course do that too:

您可以调用Todo.query()它,它会附加?token=none到您的 URL。或者,如果您更喜欢添加令牌占位符,您当然也可以这样做:

http://localhost:port/todos.json/:token

Hope this helps :)

希望这可以帮助 :)