web-dev-qa-db-ja.com

Rails 3セッションCookieを無効にする

RoR3でRESTfulAPIを記述しています。アプリケーションで「Set-Cookieヘッダー」を送信しないようにする必要があります(クライアントはauth_tokenパラメーターを使用して認証しています)。

session :offreset_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
26

Johnの回答に対するコメントで述べられているように、セッションをクリアしても、セッション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フィルターがアプリケーションスタックトレースに含まれるのを防ぐために、場合によっては完全に混乱する可能性があります。バックトレースでCookieフィルターを無音にすることをお勧めします(lib/cookie_filter.rbに配置したと仮定)。

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

組み込みオプションを使用します。

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

または同等のもの

request.session_options[:skip] = true

ここでそのドキュメントを見つけることができます http://doc.rubyists.com/rack/Rack/Session/Abstract/ID.html

39
Darwin

彼らがいつDeviseに追加したかはわかりませんが、auth_tokenを使用するときにセッションCookieの送信を無効にできる構成があるようです。

# 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]

それはうまく機能します。私が抱えていた唯一の問題は、トークンを生成/取得するために、 token_controller に最初のリクエストを送信できる必要があるということでした。つまりPOST /api/v1/tokens.json、残念ながら、そのリクエストに対してセッションCookieが返されます。

とにかく、RyanAhearnが上で書いたCookieFilterイニシャライザーを実装することになりました。

また、私のアプリにはWebフロントエンドとJSON APIの両方があるため、JSONAPIのCookieのみをフィルタリングしたかったのです。そこで、CookieFilterクラスを変更して、最初にAPIに属するリクエストを確認しました。

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

それを行うためのより良い方法があるかどうかわからない...

8
asgeo1

別の解決策:Cookieを回避するコントローラーで、次を追加します。

after_filter :skip_set_cookies_header

def skip_set_cookies_header
  request.session_options = {}
end

Apiコントローラーのセットがある場合は、これをapi_controllerクラスに設定し、他のコントローラーにapi_controllerを継承させます。

セッションoptsが空であるため、これはSet-Cookieヘッダーの設定をスキップします。

5
tommy chheng

デフォルトの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ヘッダーが送信されなくなります

4
John Douthat

Johnの回答に加えて、CSRF保護を使用している場合は、Webサービス要求に対してそれをオフにする必要があります。アプリケーションコントローラーの保護されたメソッドとして、以下を追加できます。

  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に依存します)。

3
Sayantam

私自身、セッションを宣言的にオフにできることを本当に逃しました(session :offを使用)

...したがって、私はそれを「元に戻しました」-昔ながらのレール(<= 2.2)のように使用します:

もちろん、これには独自のDevise固有のハッキングが必要になる場合があります。これは、session_offによってコントローラーでsession == nilが発生する可能性があるためです。また、2.3以降のほとんどのRails拡張機能は、怠惰なセッションを想定しているだけです。決してゼロになることはありません。

https://github.com/kares/session_off

2
kares

Imoの最善のアプローチは、Cookieセッションストアミドルウェアを削除することです。

これを行うには、これをapplication.rb(または必要に応じて特定の環境)に追加します。

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

代わりにこれを試してください

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
0
choonkeat