IT박스

AngularJS : $ resource 요청으로 인증 토큰을 보내는 방법은 무엇입니까?

itboxs 2020. 11. 27. 07:56
반응형

AngularJS : $ resource 요청으로 인증 토큰을 보내는 방법은 무엇입니까?


API에서 리소스를 요청할 때 인증 토큰을 보내고 싶습니다.

$ resource를 사용하여 서비스를 구현했습니다.

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

그리고 인증 토큰을 저장하는 서비스가 있습니다.

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

그러나 Todo모든 호출과 함께 전송되어야 하므로 access_token을 서비스 의 매개 변수로 정의하는 것을 선호합니다 . 그리고 DRY를 개선합니다. 그러나 팩토리의 모든 것은 한 번만 실행되므로 access_token은 팩토리를 정의하기 전에 사용할 수 있어야하며 나중에 변경할 수 없습니다.

동적으로 업데이트 된 요청 매개 변수를 서비스에 넣는 방법이 있습니까?


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

보시다시피 리소스는 처음에 일반적인 방식으로 정의됩니다. 내 예에서는 update. 그 후 tokenHandler.wrapAction()리소스와 작업 배열을 매개 변수로 사용 하는 메서드 의 반환으로 리소스를 덮어 씁니다 .

후자의 방법은 실제로 모든 요청에 ​​인증 토큰을 포함하도록 작업을 래핑하고 수정 된 리소스를 반환합니다. 이에 대한 코드를 살펴 보겠습니다.

.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()메서드는 매개 변수에서 리소스의 복사본을 만들고 actions배열을 반복하여 tokenWrapper()모든 작업에 대해 다른 함수를 호출 합니다. 결국 리소스의 수정 된 복사본을 반환합니다.

먼저이 tokenWrapper메서드는 기존 리소스 작업의 복사본을 만듭니다. 이 사본에는 뒤에 밑줄이 있습니다. 그래서 query()이된다 _query(). 나중에 새 메서드가 원래 query()메서드를 덮어 씁니다 . 이 새로운 메서드 _query()는 Andy Joslin이 제안한대로을 래핑 하여 해당 작업을 통해 전송되는 모든 요청에 ​​인증 토큰을 제공합니다.

이 접근 방식의 좋은 점은 모든 angularjs 리소스 (가져 오기, 쿼리, 저장 등)와 함께 제공되는 사전 정의 된 작업을 재정의하지 않고도 사용할 수 있다는 것입니다. 나머지 코드 (예 : 컨트롤러 내)에서는 기본 작업 이름을 사용할 수 있습니다.


또 다른 방법은 "마법의"Authorization 헤더를 현재 OAuth 토큰으로 대체하는 HTTP 인터셉터를 사용하는 것입니다. 아래 코드는 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';

그것에 대한 래퍼 함수를 ​​만들 수 있습니다.

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

나는이 문제도 다루어야했다. 나는 그것이 우아한 해결책이라고 생각하지 않지만 작동하고 두 줄의 코드가 있습니다.

예를 들어 SessionService에서 인증 한 후 서버에서 토큰을 얻는다고 가정합니다. 그런 다음 이러한 종류의 메서드를 호출합니다.

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

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

그 후 $ resource 및 $ http의 모든 요청에는 헤더에 토큰이 있습니다.


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

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.


I might be misunderstanding all of your question (feel free to correct me :) ) but to specifically address adding the access_token for every request, have you tried injecting the TokenHandler module into the Todo module?

// 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=none to your URL. Or if you prefer to add a token placeholder you can of course do that too:

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

Hope this helps :)


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

.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.setToken(theNewToken);

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

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

참고URL : https://stackoverflow.com/questions/11176330/angularjs-how-to-send-auth-token-with-resource-requests

반응형