0

How Rails Sessions Work

Trong lúc làm việc đôi khi ta phải giải quyết vấn đề để làm sao có thể sử dụng giá trị của 1 biến dùng ở nhiều controller, view khác nhau? Session giúp ta giải quyết vấn đề đó 1 cách đơn giản. Session là nơi hoàn hảo để lưu giá trị của biến. Nếu dữ liệu nhỏ bạn muốn giữ chúng lại trong nhiều lần request thì session rất dễ để sử dụng

session[:current_user_id] = @user.id

Vậy session là gì? Làm sao để Rails có thể biết để hiển thị dữ liệu.

Session là gì?

Là nơi lưu trữ dữ liệu trong 1 request mà bạn có thể dùng được nó ở các lần request sau Bạn có thể thiết lập session trong controller để lưu lại id của current_user lúc đăng nhập

Screenshot from 2016-10-26 18:03:04.png

Và có thể dùng nó trong controller khác, dùng bất cứ đâu nếu session chưa bị hủy hay hết hạn

Screenshot from 2016-10-26 18:04:50.png

session[:user] = @user
flash[:message] = "Data was saved successfully"

Sử dụng trong view:

<%= link_to "login", :action => 'login' unless session[:user] %>
<% if flash[:message] %>
<div><%= h flash[:message] %></div>
<% end %>

Vậy khi nào session hết hạn: Khi chúng ta logout ra khỏi ứng dụng hoặc trong 1 khoảng thời gian nào đó. Ta có thể nhìn vào options của session: Trường expires_in thông báo sau 21600 giây thì session hết hạn

[11] pry(#<CompaniesController>)> session.options[:expires_in] = 21600.seconds
=> 20800 seconds
[12] pry(#<CompaniesController>)> session.options
=> {:path=>"/",
 :domain=>nil,
 :expire_after=>nil,
 :secure=>false,
 :httponly=>true,
 :defer=>false,
 :renew=>false,
 :redis_server=>"redis://localhost:6379/0/session",
 :expires_in=>21600 seconds,
 :servers=>"redis://localhost:6379/0/session"}

Nó dường như không có gì đặc biệt nhưng phải kết hợp sự kết hợp giữa các trình duyệt của người dùng với ứng dụng Rails. Và tất cả bắt đầu với cookies

Khi 1 request được gửi lên, server có thể cài đặt 1 cookies và trả nó trở về:

~ jweiss$ curl -I http://www.google.com | grep Set-Cookie

Set-Cookie: NID=67=J2xeyegolV0SSneukSOANOCoeuDQs7G1FDAK2j-nVyaoejz-4K6aouUQtyp5B_rK3Z7G-EwTIzDm7XQ3_ZUVNnFmlGfIHMAnZQNd4kM89VLzCsM0fZnr_N8-idASAfBEdS; expires=Wed, 16-Sep-2015 05:44:42 GMT; path=/; domain=.google.com; HttpOnly

Trình duyệt sẽ lưu những cookies của bạn. Mỗi khi bạn thực hiện một yêu cầu, trình duyệt của bạn sẽ gửi lại cookie cho máy chủ:

...
> GET / HTTP/1.1
> User-Agent: curl/7.37.1
> Host: www.google.com
> Accept: */*
> Cookie: NID=67=J2xeyegolV0SSneukSOANOCoeuDQs7G1FDAK2j-nVyaoejz-4K6aouUQtyp5B_rK3Z7G-EwTIzDm7XQ3_ZUVNnFmlGfIHMAnZQNd4kM89VLzCsM0fZnr_N8-idASAfBEdS; expires=Wed, 16-Sep-2015 05:44:42 GMT; path=/; domain=.google.com; HttpOnly
...

Nhiều cookie gần như vô nghĩa. Bởi vì thông tin trong cookie không có nghĩa là cho người sử dụng. ứng dụng Rails của bạn là phụ trách tìm ra những gì một cookie có nghĩa. Ứng dụng của bạn cài đặt nó, vì vậy ứng dụng của bạn có thể đọc nó.

Nơi lưu trữ của session

Rails cung cấp 1 số cơ chế lưu trữ session:

  • ActionDispatch::Session::CookieStore - Lưu mọi thứ trên client.

  • ActionDispatch::Session::CacheStore - Lưu dữ liệu trong Rails cache.

  • ActionDispatch::Session::ActiveRecordStore - Lưu dữ liệu trong một cơ sở dữ liệu sử dụng Active Record.(yêu cầu gem activerecord-session_store).

  • ActionDispatch::Session::MemCacheStore - Lưu dữ liệu trong một memcached cluster (đây là một cách làm cũ; xem xét sử dụng CacheStore thay thế).

Tất cả session chứa một cookie để lưu một ID duy nhất cho mỗi session (bạn phải sử dụng một cookie, Rails sẽ không cho phép bạn truyền session ID trong URL vì thế rất là thiếu an toàn) Phổ biến nhất là dùng ActionDispatch::Session::CookieStore vì nó làm tăng tốc độ của ứng dụng nhưng vấn đề bảo mật đáng phải quan tâm khi:

  • Cookies chỉ có thể chứad 1 giới hạn đó là 4kb. Điều đó có nghĩa là không nên sử dụng để lưu trữ 1 lượng lớn dữ liệu trong một session. Lưu tữ id của session thì hoàn toàn dùng được ok
  • Mọi người dùng đều có thể nhìn thấy thứ bạn lưu trong session bởi vì nó được lưu 1 cách rất rõ ràng. Vì vậy, tất nhiên, bạn không muốn lưu trữ bất kỳ bí mật ở đây. Để ngăn chặn phiên băm giả mạo, một digest được tính từ phiên với một bí mật phía máy chủ (secrets.secret_token) và chèn vào phần đầu của các cookie. Tuy nhiên, kể từ khi Rails 4, nơi lưu trữ mặc định của session là EncryptedCookieStore. Với EncryptedCookieStore phiên được mã hóa trước khi được lưu trữ trong một cookie. Điều này ngăn cản người dùng truy cập và giả mạo nội dung của các tập tin cookie. Như vậy phiên trở thành một nơi an toàn hơn để lưu trữ dữ liệu. Các mã hóa được thực hiện bằng cách sử dụng phía máy chủ bí mật secrets.secret_key_base phím được lưu giữ trong config / secrets.yml.

Các method của session

  • session.to_hash: Sẽ cho được danh sách các session tồn tại trong phiên làm việc của bạn. Ví dụ ta có thể xem được biến session["scholarship_event"] được lưu trong session.
[8] pry(#<User::PreviewController>)> session.to_hash
=> {"warden.user.user.key"=>[[1], "$2a$11$FH0.gmhtnSFwLJs37T7cru"],
 "warden.user.user.session"=>{"last_request_at"=>1477481582},
 "flash"=>{"discard"=>[], "flashes"=>{"notice"=>"ログインに成功しました。"}},
 "scholarship_event"=>
  #<Event:0x007fcaa1e55ec0
   id: 1,
   name_en: "event_0",
   name_ja: "イベント_0",
   created_at: Wed, 12 Oct 2016 09:54:27 UTC +00:00,
   updated_at: Wed, 26 Oct 2016 09:51:31 UTC +00:00,
   "job"=>
  #<Job:0x007fcaa1e55ec0
   id: 1,
   name: "Job_0",
   created_at: Wed, 12 Oct 2016 09:54:27 UTC +00:00,
   updated_at: Wed, 26 Oct 2016 09:51:31 UTC +00:00
 "_csrf_token"=>"AclYjwF5ZiyMGQ9AvPwoA/79ZClnjGOsr6HKADPEUMY="}

  • session.delete(key) Ví dụ ta muốn xóa session["job"]:
session.delete("job")

Ngoài ra còn nhiều method hỗ trợ như:

  • clear ⇒ Object

  • delete(key) ⇒ Object

  • destroy ⇒ Object

  • empty? ⇒ Boolean

  • exists? ⇒ Boolean

  • fetch(key, default = Unspecified, &block) ⇒ Object

  • has_key?(key) ⇒ Boolean (also: #key?, #include?)

  • id ⇒ Object

  • initialize(by, env) ⇒ Session constructor

  • spect ⇒ Object

  • keys ⇒ Object

  • loaded? ⇒ Boolean

  • merge!(other) ⇒ Object

  • options ⇒ Object

  • to_hash ⇒ Object

  • update(hash) ⇒ Object

  • values ⇒ Object

Chúng ta có thể tùy chỉnh sử dụng session trong controller bằng cách:

session :off                           # turn session management off
session :off, :only => :action      # only for this :action
session :off, :except => :action    # except for this action

session :only => :foo,              # only for :foo when doing HTTPS
        :session_secure => true

session :off, :only=>:foo, # off for foo,if uses as Web Service
        :if => Proc.new { |req| req.parameters[:ws] }

Cách làm việc của session

Ta có thể hình dung cách làm việc của session như sau:

  1. Khi bạn gọi session[:current_user_id] = 1 trong app, và session đó chưa từng tồn tại
  2. Rails sẽ tạo một bản ghi trong bảng sessions với id của session là random. Ví dụ: id = 09497d46978bf6f32265fefb5cc52264
  3. Nó sẽ lưu trữ {current_user_id: 1} (Base64-encoded) vào trong dữ liệu của session
  4. Và nó sẽ trả lại sesion ID đã được tạo 09497d46978bf6f32265fefb5cc52264 để trình duyệt có thể sử dụng Set-Cookies

Và những lần sau khi khi request lên 1 trang nào đó

  1. Trình duyệt gửi cookies đó cho ứng dụng của bạn bằng cách sử dụng Cookie. Ví dụ:
Cookie: _my_app_session=09497d46978bf6f32265fefb5cc52264;
path=/; HttpOnly
  1. Khi bạn gọi session[:current_user_id] : Ứng dụng sẽ lấy session ID ra khỏi cookie và tìm trong sessions table để lấy dữ liệu

Dù bạn lưu session ở đâu đi chăng nữa thì cookie chỉ chứa một session ID và ứng dụng rails sẽ tìm kiếm data dựa trên ID

Mong bài viết sẽ hỗ trợ bạn hiểu thêm về session và cách làm việc của session

Nguồn tham khảo

http://www.rubydoc.info/docs/rails/ActionDispatch/Request/Session

https://gist.github.com/kienbt01359/7010304

https://www.tutorialspoint.com/ruby-on-rails/rails-session-cookies.html

http://guides.rubyonrails.org/security.htm

http://www.justinweiss.com/articles/how-rails-sessions-work/


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.