Rails 3 disabilitazione dei cookie di sessione

Ho scritto API RESTful su RoR 3. Devo fare in modo che la mia applicazione non invii “Set-Cookie header” (i client stanno autorizzando l’uso del parametro auth_token).

Ho provato a usare session :off e reset_session ma non ha alcun senso. Sto usando devise come framework di autenticazione.

Ecco il mio ApplicationController

 class ApplicationController  :session_required? session :off #, :unless => :session_required? skip_before_filter :verify_authenticity_token before_filter :access_control_headers! def options render :text => "" end private def access_control_headers! response.headers["Access-Control-Allow-Origin"] = "*" response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS" response.headers["Access-Control-Allow-Credentials"] = "true" response.headers["Access-Control-Allow-Headers"] = "Content-type" end def session_required? !(params[:format] == 'xml' or params[:format] == 'json') end end 

Come menzionato in un commento alla risposta di John, la cancellazione della sessione non impedirà l’invio del cookie di sessione. Se si desidera rimuovere completamente il cookie dall’invio, è necessario utilizzare il middleware Rack.

 class CookieFilter def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) # use only one of the next two lines # this will remove ALL cookies from the response headers.delete 'Set-Cookie' # this will remove just your session cookie Rack::Utils.delete_cookie_header!(headers, '_app-name_session') [status, headers, body] end end 

Usalo creando un inizializzatore con il seguente corpo:

 Rails.application.config.middleware.insert_before ::ActionDispatch::Cookies, ::CookieFilter 

Per evitare che il filtro dei cookie finisca nelle tracce dello stack dell’applicazione, che a volte può essere completamente confuso, potresti volerlo silenziare nel backtrace (supponendo che tu lo metta in lib/cookie_filter.rb ):

 Rails.backtrace_cleaner.add_silencer { |line| line.start_with? "lib/cookie_filter.rb" } 

Usa l’ opzione integrata .

 env['rack.session.options'][:skip] = true 

o l’equivalente

 request.session_options[:skip] = true 

Puoi trovare la documentazione qui http://doc.rubyists.com/rack/Rack/Session/Abstract/ID.html

Non sono sicuro di averlo aggiunto a Devise, ma sembra che ci sia una configurazione che ti consentirà di disabilitare l’invio del cookie di sessione quando utilizzi un token di autenticazione:

 # By default Devise will store the user in session. You can skip storage for # :http_auth and :token_auth by adding those symbols to the array below. # Notice that if you are skipping storage for all authentication paths, you # may want to disable generating routes to Devise's sessions controller by # passing :skip => :sessions to `devise_for` in your config/routes.rb config.skip_session_storage = [:http_auth, :token_auth] 

Funziona bene. L’unico problema che avevo era che dovevo ancora essere in grado di fare una richiesta iniziale al mio token_controller per generare / recuperare il token. Ie POST /api/v1/tokens.json , che sfortunatamente causerebbe il ritorno di un cookie di sessione per quella richiesta.

Così ho finito per implementare l’ CookieFilter che Ryan Ahearn ha scritto sopra comunque.

Inoltre, poiché la mia app ha sia un front-end Web che un API JSON, volevo solo filtrare i cookie per l’API JSON. Così ho modificato la class CookieFilter per controllare innanzitutto le richieste appartenute all’api:

 if env['PATH_INFO'].match(/^\/api/) Rack::Utils.delete_cookie_header!(headers, '_myapp_session') end 

Non sono sicuro se c’è un modo migliore per farlo …

Un’altra soluzione: nel controller che si desidera evitare i cookie, aggiungere questo:

 after_filter :skip_set_cookies_header def skip_set_cookies_header request.session_options = {} end 

Se si dispone di un set di controller API, impostarlo in una class api_controller e consentire agli altri controller di ereditare l’api_controller.

Salta l’impostazione dell’intestazione Set-Cookie poiché l’opzione di sessione è vuota.

Il CookieSessionStore predefinito non invia un’intestazione “Set-Cookie” a meno che non venga aggiunto qualcosa alla sessione. C’è qualcosa nel tuo stack che scrive sulla sessione? (probabilmente è Devise)

session :off è stato deprecato:

 def session(*args) ActiveSupport::Deprecation.warn( "Disabling sessions for a single controller has been deprecated. " + "Sessions are now lazy loaded. So if you don't access them, " + "consider them off. You can still modify the session cookie " + "options with request.session_options.", caller) end 

Se qualcosa nel tuo stack sta impostando le informazioni sulla sessione, puoi cancellarlo usando session.clear modo:

 after_filter :clear_session def clear_session session.clear end 

Che impedirà l’invio dell’intestazione Set-Cookie

Oltre alla risposta di John, se si utilizza la protezione CSRF, è necessario distriggersrla per le richieste di servizi Web. È ansible aggiungere quanto segue come metodo protetto nel controller dell’applicazione:

  def protect_against_forgery? unless request.format.xml? or request.format.json? super end end 

In questo modo, le richieste HTML continuano a utilizzare CSRF (o no – dipende da config.action_controller.allow_forgery_protection = true/false nell’ambiente).

Mi sono davvero perso la possibilità di distriggersre dichiarativamente le sessioni (usando la session :off )

… quindi l’ho portato “indietro” – usalo come in plain-old-rails (<= 2.2):

ovviamente questo potrebbe richiedere un ulteriore hacking specifico di Devise, dal momento che session_off potrebbe causare session == nil in un controller, e la maggior parte delle estensioni di rail dal 2.3 assumono semplicemente una sessione lazy che non sarà mai nulla.

https://github.com/kares/session_off

L’approccio migliore è semplicemente quello di rimuovere il middleware del cookie session store.

Per fare ciò, aggiungilo al tuo application.rb (o ad un ambiente specifico se necessario):

 # No session store config.middleware.delete ActionDispatch::Session::CookieStore 

Prova questo invece

 after_filter :skip_set_cookies_header def skip_set_cookies_header session.instance_variable_set('@loaded', false) end 

O ancora meglio, rimuovi sempre intestazione Set-Cookie quando i dati della sessione non cambiano

 before_filter :session_as_comparable_array # first before_filter after_filter :skip_set_cookies_header # last after_filter def session_as_comparable_array(obj = session) @session_as_comparable_array = case obj when Hash obj.keys.sort_by(&:to_s).collect{ |k| [k, session_as_comparable_array(obj[k])] } when Array obj.sort_by(&:to_s).collect{ |k| session_as_comparable_array(k) } else obj end end def skip_set_cookies_header session.instance_variable_set('@loaded', false) if (@session_as_comparable_array == session_as_comparable_array) end