2009-08-07 5 views
14

Z jakiegoś powodu otrzymuję InvalidAuthenticityToken podczas wysyłania postów do mojej aplikacji przy użyciu json lub xml. Rozumiem, że szyny powinny wymagać tokenu uwierzytelniania tylko dla żądań html lub js, a zatem nie powinienem napotykać tego błędu. Jedynym rozwiązaniem, jakie znalazłem do tej pory, jest wyłączenie protect_from_forgery dla każdej akcji, do której chciałbym uzyskać dostęp za pośrednictwem interfejsu API, ale nie jest to idealne z oczywistych powodów. Myśli?rails - InvalidAuthenticityToken dla żądań json/xml

def create 
    respond_to do |format| 
     format.html 
     format.json{ 
      render :json => Object.create(:user => @current_user, :foo => params[:foo], :bar => params[:bar]) 
     } 
     format.xml{ 
      render :xml => Object.create(:user => @current_user, :foo => params[:foo], :bar => params[:bar]) 
     } 
    end 
end 

i to, co mam w dziennikach ilekroć mijam wniosek do działania:

Processing FooController#create to json (for 127.0.0.1 at 2009-08-07 11:52:33) [POST] 
Parameters: {"foo"=>"1", "api_key"=>"44a895ca30e95a3206f961fcd56011d364dff78e", "bar"=>"202"} 

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken): 
    thin (1.2.2) lib/thin/connection.rb:76:in `pre_process' 
    thin (1.2.2) lib/thin/connection.rb:74:in `catch' 
    thin (1.2.2) lib/thin/connection.rb:74:in `pre_process' 
    thin (1.2.2) lib/thin/connection.rb:57:in `process' 
    thin (1.2.2) lib/thin/connection.rb:42:in `receive_data' 
    eventmachine (0.12.8) lib/eventmachine.rb:242:in `run_machine' 
    eventmachine (0.12.8) lib/eventmachine.rb:242:in `run' 
    thin (1.2.2) lib/thin/backends/base.rb:57:in `start' 
    thin (1.2.2) lib/thin/server.rb:156:in `start' 
    thin (1.2.2) lib/thin/controllers/controller.rb:80:in `start' 
    thin (1.2.2) lib/thin/runner.rb:174:in `send' 
    thin (1.2.2) lib/thin/runner.rb:174:in `run_command' 
    thin (1.2.2) lib/thin/runner.rb:140:in `run!' 
    thin (1.2.2) bin/thin:6 
    /opt/local/bin/thin:19:in `load' 
    /opt/local/bin/thin:19 

Odpowiedz

12

miałem podobną sytuację i problem było to, że nie było wysłanie przez prawe treści wpisz nagłówki - prosiłem o text/json i powinienem był żądać application/json.

użyłem curl następujące czynności, aby przetestować aplikację (zmiany w razie potrzeby):

curl -H "Content-Type: application/json" -d '{"person": {"last_name": "Lambie","first_name": "Matthew"}}' -X POST http://localhost:3000/people.json -i 

Albo można zapisać JSON do lokalnego pliku i nazwać curl takiego:

curl -H "Content-Type: application/json" -v -d @person.json -X POST http://localhost:3000/people.json -i 

When Zmieniłem nagłówki typu zawartości w prawo application/json wszystkie moje problemy zniknęły i nie potrzebowałem już wyłączać ochrony przed fałszerstwem.

+0

to naprawdę nie działa dla aplikacji internetowych, choć. Przeglądarki nie mogą się kręcić. = ( – NullVoxPopuli

+3

Chodzi o to, że debugowałem problem z curl i stwierdziłem, że ważne jest wysłanie odpowiedniego nagłówka Content-Type – mlambie

19

Po włączeniu protect_from_forgery, Rails wymaga tokena uwierzytelnienia dla wszystkich żądań innych niż GET. Railsy automatycznie zawierają token autentyczności w formularzach utworzonych za pomocą helperów formularzy lub linków utworzonych za pomocą helperów AJAX - w normalnych przypadkach nie będziesz musiał o tym myśleć.

Jeśli nie używasz wbudowanego formularza Railsów lub pomocników AJAX (być może robisz niewzruszony JS lub używasz JS MVC), musisz sam ustawić token po stronie klienta i wysłać razem z danymi podczas wysyłania żądania POST. Można by postawić taką linię w <head> swojej układ:

<%= javascript_tag "window._token = '#{form_authenticity_token}'" %> 

Następnie czynność AJAX byłoby zakładać token z innymi danymi (np z jQuery):

$.post(url, { 
    id: theId, 
    authenticity_token: window._token 
}); 
+1

Optimate powinien przyjąć tę odpowiedź. Przy okazji, jeśli użyjesz '$ .ajax', musisz umieść token autentyczności w polu danych w następujący sposób: '$ .ajax ({data: {authenticity_token: window._token}})' –

2

dodanie do odpowiedź andymism może użyć tego, aby zastosować domyślne włączenie token w każde żądanie POST:

$(document).ajaxSend(function(event, request, settings) { 
    if (settings.type == 'POST' || settings.type == 'post') { 
     settings.data = (settings.data ? settings.data + "&" : "") 
      + "authenticity_token=" + encodeURIComponent(window._token); 
    } 
}); 
+1

Punkt api_key polega na uniknięciu auth_token – NullVoxPopuli

4

Innym sposobem jest unikanie verify_authenticity_token użyciu skip_before_filter w swojej Aplikacja Rails:

skip_before_action :verify_authenticity_token, only: [:action1, :action2] 

Pozwoli to na wykonywanie zawinięć.

2

Aby dodać odpowiedź Fernando, jeśli kontroler reaguje zarówno json i HTML, można użyć:

 skip_before_filter :verify_authenticity_token, if: :json_request? 
+1

Czy to nie jest zagrożenie dla bezpieczeństwa? –

10

To jest taka sama jak @user1756254's answer ale w Rails 5 trzeba użyć nieco więcej innej składni :

protect_from_forgery unless: -> { request.format.json? } 

Źródło: http://api.rubyonrails.org/v5.0/classes/ActionController/RequestForgeryProtection.html

+3

Czy to nie jest zagrożenie dla bezpieczeństwa? –

+1

@WylliamJudd nie, wyłączenie go tylko dla JSON nie stanowi zagrożenia bezpieczeństwa, ponieważ celem zabezpieczenia przed fałszerstwem jest ochrona formularzy HTML przesłanych z innych stron – edwardmp

+2

Kiedy oficjalna dokumentacja mówi: _ jeśli budujesz API powinieneś zmienić metodę zabezpieczenia przed fałszerstwem w ApplicationController (domyślnie:: exception) '_, wtedy jest wystarczająco bezpieczny –