Rails 3 отключает файлы cookie сеанса
У меня есть RESTful API, написанный на RoR 3.
Я должен заставить мое приложение не отправлять "Set-Cookie header" (клиенты авторизуются с использованием параметра auth_token).
Я попытался использовать session :off
и reset_session
, но это не имеет никакого смысла.
Я использую devise
в качестве среды проверки подлинности.
Вот мой ApplicationController
class ApplicationController < ActionController::Base
before_filter :reset_session #, :unless => :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
Ответы
Ответ 1
Как упоминается в комментарии к ответу Джона, очистка сеанса не будет препятствовать отправке cookie сеанса. Если вы хотите полностью удалить cookie из отправки, вам нужно использовать промежуточное ПО 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
Используйте его, создав инициализатор со следующим телом:
Rails.application.config.middleware.insert_before ::ActionDispatch::Cookies, ::CookieFilter
Чтобы фильтр cookie не попадал в трассировки стека приложений, что может иногда быть непонятным, вы можете отключить его в backtrace (предположив, что вы положили его в lib/cookie_filter.rb
):
Rails.backtrace_cleaner.add_silencer { |line| line.start_with? "lib/cookie_filter.rb" }
Ответ 2
Используйте встроенную опцию.
env['rack.session.options'][:skip] = true
или эквивалентный
request.session_options[:skip] = true
Здесь вы можете найти документацию http://doc.rubyists.com/rack/Rack/Session/Abstract/ID.html
Ответ 3
Я не уверен, когда они добавили его в Devise, но, похоже, есть конфигурация, которая позволит вам отключить отправку файла cookie сеанса при использовании auth_token:
# 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 sessions controller by
# passing :skip => :sessions to `devise_for` in your config/routes.rb
config.skip_session_storage = [:http_auth, :token_auth]
Это хорошо работает. Единственная проблема, с которой я столкнулся, заключалась в том, что мне все еще нужно было сделать первоначальный запрос для моего token_controller, чтобы генерировать/извлекать токен. То есть POST /api/v1/tokens.json
, что, к сожалению, приведет к возврату куки файла сеанса для этого запроса.
Итак, я закончил реализацию intializer CookieFilter
, который Райан Ahearn написал выше в любом случае.
Кроме того, поскольку мое приложение имеет как веб-интерфейс, так и JSON api, я только хотел отфильтровать файлы cookie для JSON api. Поэтому я изменил класс CookieFilter
, чтобы сначала проверить запросы, принадлежащие api:
if env['PATH_INFO'].match(/^\/api/)
Rack::Utils.delete_cookie_header!(headers, '_myapp_session')
end
Не уверен, есть ли лучший способ сделать это...
Ответ 4
Другое решение:
В контроллере вы хотите избежать куки файлов, добавьте это:
after_filter :skip_set_cookies_header
def skip_set_cookies_header
request.session_options = {}
end
Если у вас есть набор контроллеров api, установите это в классе api_controller и пусть ваши другие контроллеры наследуют api_controller.
Это пропускает значение Set-Cookie, поскольку выбор сеанса пуст.
Ответ 5
CookieSessionStore по умолчанию не отправляет заголовок "Set-Cookie", если только что-то не добавлено в сеанс. Что-то в вашем стеке записывается на сеанс? (это, вероятно, Devise)
session :off
устарел:
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
Если что-то в вашем стеке устанавливает информацию о сеансе, вы можете очистить его, используя session.clear
, как показано ниже:
after_filter :clear_session
def clear_session
session.clear
end
Это предотвратит отправку заголовка Set-Cookie
Ответ 6
В ответ на ответ Джона, если вы используете CSRF-защиту, вам нужно будет отключить это для запросов веб-сервисов. Вы можете добавить следующее в качестве защищенного метода в контроллер приложения:
def protect_against_forgery?
unless request.format.xml? or request.format.json?
super
end
end
Таким образом, HTML-запросы по-прежнему используют CSRF (или нет - зависит от config.action_controller.allow_forgery_protection = true/false
в среде).
Ответ 7
Я сам действительно пропустил возможность декларативно отключить сеансы (используя session :off
)
... таким образом, я вернул его "назад" - используйте его точно так же, как в простых старых рельсах (< = 2,2):
чем, конечно, это может потребовать некоторого дополнительного специального взлома Devise, поскольку session_off может вызвать session == nil
в контроллере, а большинство расширений rails с 2.3 просто предполагают ленивый сеанс, который никогда не будет ничем.
https://github.com/kares/session_off
Ответ 8
Ими лучшим подходом является простое удаление промежуточного программного обеспечения хранилища файлов cookie.
Для этого добавьте это в свой application.rb(или в конкретную среду, если необходимо):
# No session store
config.middleware.delete ActionDispatch::Session::CookieStore
Ответ 9
Попробуйте это вместо
after_filter :skip_set_cookies_header
def skip_set_cookies_header
session.instance_variable_set('@loaded', false)
end
Или даже лучше, всегда удаляйте заголовок Set-Cookie, когда данные сеанса не изменялись
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