AngularJS:如何发送具有$资源请求的authentication令牌?

我想从我的API请求资源时发送一个授权令牌。

我没有使用$ resource实现一个服务:

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

我有一个存储auth令牌的服务:

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

我想从tokenHandler.get发送令牌,通过Todo服务发送每个请求。 我可以通过将其发送到特定行动的呼叫中来发送它。 例如这个工程:

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

但是我宁愿将access_token定义为Todo服务中的一个参数,因为它必须随每次调用一起发送。 并改善干燥。 但是工厂里的所有东西都只执行一次,所以access_token在定义工厂之前必须可用,之后不能更改。

有没有办法把一个dynamic更新的请求参数在服务中?

感谢Andy Joslin。 我select了包装资源行动的想法。 现在资源的服务如下所示:

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

正如你所看到的,资源首先是通常定义的。 在我的示例中,这包括一个名为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; }); 

正如你所看到的, wrapActions()方法从它的参数中创build资源的一个副本,并通过actions数组循环来为每个动作调用另一个函数tokenWrapper() 。 最后,它返回资源的修改副本。

tokenWrapper方法首先创build一个预先存在的资源操作的副本。 该副本具有尾部下划线。 所以query()变成_query() 。 之后一个新的方法覆盖原来的query()方法。 正如Andy Joslin所build议的,这个新的方法包装了_query() ,为每个通过该动作发送的请求提供auth令牌。

这种方法的好处是,我们仍然可以使用每个angularjs资源(get,query,save等)附带的预定义操作,而不必重新定义它们。 在代码的其余部分(例如在控制器中),我们可以使用默认的操作名称。

另一种方法是使用HTTP拦截器,用当前的OAuth令牌replace“神奇”授权标头。 下面的代码是特定于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'); }); 

我非常喜欢这种方法:

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'; 

你可以为它创build一个包装函数。

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

我也必须处理这个问题。 我不认为如果这是一个优雅的解决scheme,但它的工作,并有2行代码:

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

  angular.module('xxx.sessionService', ['ngResource']). factory('SessionService', function( $http, $rootScope) { //... function setHttpProviderCommonHeaderToken(token){ $http.defaults.headers.common['X-AUTH-TOKEN'] = token; } }); 

在这之后,$ resource和$ http的所有请求都会在其头部有令牌。

另一个解决scheme是使用resource.bind(additionalParamDefaults),它返回一个绑定了额外参数的资源的新实例

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

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

我可能会误解你所有的问题(随时纠正我:)),但具体地址解决每个请求添加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 }); }) 

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

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

希望这可以帮助 :)

遵循您接受的答案,我会build议扩展资源以设置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; }]); 

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

todo.setToken(theNewToken);

我想做的另一个改变是,如果在wrapActions它们是空的,则允许默认的动作:

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