with then example javascript authentication angularjs

javascript - then - http post angularjs example



AngularJS: ¿Cómo enviar auth token con $ resource requests? (8)

Deseo enviar un token de autenticación cuando solicite un recurso de mi API.

Implementé un servicio usando $ resource:

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

Y tengo un servicio que almacena el token de autenticación:

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

Me gustaría enviar el token de tokenHandler.get con cada solicitud enviada a través del servicio Todo . Pude enviarlo colocándolo en la llamada de una acción específica. Por ejemplo, esto funciona:

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

Pero preferiría definir access_token como un parámetro en el servicio Todo , ya que debe enviarse con cada llamada. Y para mejorar DRY. Pero todo en la fábrica se ejecuta solo una vez, por lo que access_token debería estar disponible antes de definir la fábrica y no puede cambiar después.

¿Hay alguna manera de poner un parámetro de solicitud actualizado dinámicamente en el servicio?


Gracias a Andy Joslin. Elegí su idea de envolver las acciones de recursos. El servicio para el recurso se ve así ahora:

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

Como puede ver, el recurso se define de la manera habitual en primer lugar. En mi ejemplo, esto incluye una acción personalizada llamada update . Posteriormente, el recurso se sobrescribe con el método tokenHandler.wrapAction() que toma el recurso y una serie de acciones como parámetros.

Como es de esperar, el último método ajusta las acciones para incluir el token de autenticación en cada solicitud y devuelve un recurso modificado. Así que echemos un vistazo al código para eso:

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

Como puede ver, el método wrapActions() crea una copia del recurso de sus parámetros y realiza un bucle a través de la matriz de actions para llamar a otra función tokenWrapper() para cada acción. Al final, devuelve la copia modificada del recurso.

El método tokenWrapper primero crea una copia de la acción del recurso preexistente. Esta copia tiene un guión bajo final. Entonces query() convierte en _query() . Luego, un nuevo método sobrescribe el método query() original. Este nuevo método envuelve _query() , como lo sugiere Andy Joslin, para proporcionar el token de autenticación con cada solicitud enviada a través de esa acción.

Lo bueno de este enfoque es que todavía podemos usar las acciones predefinidas que vienen con cada recurso angularjs (get, query, save, etc.), sin tener que redefinirlos. Y en el resto del código (dentro de los controladores, por ejemplo) podemos usar el nombre de acción predeterminado.



Otra forma es usar un interceptor HTTP que reemplace un encabezado de Autorización "mágico" con el token OAuth actual. El siguiente código es específico de OAuth, pero remediarlo es un ejercicio simple para el lector.

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


Otra solución sería usar resource.bind (additionalParamDefaults), que devuelve una nueva instancia del recurso enlazado con parámetros adicionales

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

Se llamará a la función access_token cada vez que se llame a alguna de las acciones en el recurso.


Podría crear una función de envoltura para ello.

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


Puede que esté malinterpretando toda su pregunta (no dude en corregirme :)) pero para abordar específicamente la adición de access_token para cada solicitud, ¿ha intentado inyectar el módulo TokenHandler en el módulo 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 }); })

Puede llamar a Todo.query() y anexará ?token=none a su URL. O si prefiere agregar un marcador de posición de token, puede hacerlo también:

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

Espero que esto ayude :)


Siguiendo su respuesta aceptada, propondría ampliar el recurso para configurar el token con el objeto 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; }]);

De esta forma, no es necesario importar el TokenHandler cada vez que quiera usar el objeto Todo y puede usar:

todo.setToken(theNewToken);

Otro cambio que haría sería permitir acciones predeterminadas si están vacías en wrapActions :

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


Tuve que lidiar con este problema también. No creo que sea una solución elegante, pero funciona y hay 2 líneas de código:

Supongo que obtienes tu token de tu servidor después de una autenticación en SessionService, por ejemplo. Entonces, llama a este tipo de método:

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

Después de eso, todas sus solicitudes de $ resource y $ http tendrán token en su encabezado.