[RAILS] TÌM HIỂU VỀ SESSION VÀ CÁCH THỨC LƯU TRỮ SESSION DATA

1. Session là gì ?

Session là được hiểu là 1 phiên làm việc trong đó người sử dụng giao tiếp với 1 ứng dụng. Session bắt đầu khi người sử dụng truy cập vào ứng dụng lần đầu tiên, và kết thúc khi người sử dụng thoát khỏi ứng dụng. Một session thường được gắn với 1 mã số định danh (Session ID) và 1 hash chứa 1 số thông tin nhất định của người dùng. Trong ngữ cảnh ứng dụng web, đó có thể là định danh của người dùng, những bài viết mà người đó đã đọc qua hay những món hàng mà người đó đã xem và có ý định mua…

Session ID

Session ID là một chuỗi kí tự ngẫu nhiên dùng để phân biệt 1 session với các session khác. Trong Rails, Session ID là 1 chuỗi 32 kí tự, là kết quả của việc mã hóa MD5 1 chuỗi kí tự ngẫu nhiên khác được tạo thành từ: thời gian hiện tại, 1 sỗ ngẫu nhiên giữa 0 và 1, process id của trình dịch Ruby và 1 chuỗi cố định. Với Rails 4 bạn có lấy giá trị của Session ID trong controller như sau:

[1] pry(#)> session.id
=> "06383951600dd0fc8713fafd63142fce"

Session ID được client lưu trữ trong cookie và gửi kèm theo mỗi request đến server. Tên của cookie dùng để lưu trữ Session ID có thể được set thông qua việc set giá trị cho biến key ở file config/initializers/session_store.rb.

Rails.application.config.session_store :cookie_store, key: '_my_key'

Nếu người dùng truy cập vào website của bạn sau đó xem cookie của trình duyệt thì sẽ thấy có 1 cookie tên là _my_key như dưới (nội dung của cookie này sẽ được giải thích ở các phần sau).

2. Cách thức lưu trữ session data

Ở phần này, bài viết xin giới thiệu với các bạn 3 kiểu lưu trữ session data phổ biến của Rails 4 đó là CookieStore, ActiveRecordStoreRedisStore.

2.1 CookieStore

CookieStore là kiểu lưu trữ session data mặc định của Rails kể từ Rails 2. CookieStore không chỉ lưu trữ Session ID mà lưu toàn bộ session data trong cookie ở phía client. Do đó server không lưu trữ bất kì thông tin nào liên quan đến session mà sẽ đọc từ cookie do client gửi kèm theo mỗi request. Kiểu lưu trữ này có ưu điểm là cải thiện tốc độ của ứng dụng web nhưng cũng có nhược điểm là không thể lưu trữ được lượng lớn thông tin do cookies có giới hạn là 4KB.

Mặc định, Rails sẽ mã hóa session data bằng ActiveSupport::MessageEncryptor trước khi gửi xuống trình duyệt để đảm bảo tính bảo mật. Dưới đây là cách thức mã hóa đó.

key_generator = ActiveSupport::KeyGenerator.new(SECRET_KEY_BASE, iterations: 1000)
secret = key_generator
  .generate_key(Rails.application.config.action_dispatch.encrypted_cookie_salt)
sign_secret = key_generator
  .generate_key(Rails.application.config.action_dispatch.encrypted_signed_cookie_salt)
encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret)
encryptor.encrypt_and_sign session_data

Ở đây SECRET_KEY_BASE chính là secret_key_base được định nghĩa trong file config/secrets.rb, session_data chính là dữ liệu của session dưới dạng hash và có thể được lấy trong controller bằng cách

session_data = session.to_hash

2.2 ActiveRecordStore

ActiveRecordStore là hình thức quản lý session bằng ActiveRecord, mọi dữ liệu về session sẽ được lưu trong database như những model khác trong ứng dụng web. Để sử dụng ActiveRecordStore, đầu tiên bạn cần thêm gem activerecord-session_store vào trong Gemfile và chạy bundle install

gem 'activerecord-session_store'

Tạo 1 file migration mới để tạo bảng lưu trữ dữ liệu của session

rails generate active_record:session_migration

Chạy migration

rake db:migrate

Trong database sẽ có 1 bảng mới được tạo ra có tên là sessions và có cấu trúc như dưới:

mysql> desc sessions;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| session_id | varchar(255) | NO   | UNI | NULL    |                |
| data       | text         | YES  |     | NULL    |                |
| created_at | datetime     | YES  |     | NULL    |                |
| updated_at | datetime     | YES  | MUL | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

Bạn cũng có thể xem cấu trúc của bảng trên thông qua model tương ứng trong Rails như sau:

[1] pry(main)> ActiveRecord::SessionStore::Session
=> ActiveRecord::SessionStore::Session(id: integer, session_id: string, data: text, created_at: datetime, updated_at: datetime)

(Lưu ý: ngoại trừ CookieStore thì những dạng lưu trữ khác như ActiveRecordStore và RedisStore sẽ không chứa Session ID trong session data)

Cấu hình session store trong file config/initializers/session_store.rb và khởi động lại server

Rails.application.config.session_store :active_record_store, key: '_my_key'

Lúc này nếu bạn kiểm tra lại trên trình duyệt sẽ thấy chỉ còn Session ID được lưu trữ ở cookie chứ không phải toàn bộ session data như trường hợp của CookieStore nữa.

Và cuối cùng là cách thức mã hóa dữ liệu: ActiveRecordStore serialize dữ liệu bằng module Marshal sau đó encode dạng base 64 và lưu vào trong database.

Base64.strict_encode64(Marshal.dump(session_data))

2.3 RedisStore

RedisStore về ý tưởng cũng giống như ActiveRecordStore đó là lưu trữ Session ID ở cookie phía client và lưu trữ session data trong database phía server. Tuy nhiên thay vì sử dụng những cơ sở dữ liệu kiểu truyền thống như MySQL, PostgreSQL… thì nó sử dụng Redis – một cơ sở dữ liệu dạng key-value có điểm mạnh là tốc độ đọc và ghi. Để sử dụng RedisStore, bạn thêm gem redis-rails vào trong Gemfile và chạy bundle install

gem 'redis-rails'

Cấu hình session store trong file config/initializers/session_store.rb và khởi động lại server

Rails.application.config.session_store :redis_store, key: '_my_key'

RedisStore mã hóa dữ liệu không quá phức tạp, nó chỉ serialize dữ liệu bằng Marshal rồi lưu trực tiếp vào database.

Marshal.dump(session_data)

Bạn có thể connect tới database với redis-cli để xem những session đang được lưu trữ:

[email protected]:~/projects/myapp$ redis-cli
127.0.0.1:6379> KEYS *
1) "5bc8c00a1ce61cd873fb2d83f5af7366"
127.0.0.1:6379> GET "5bc8c00a1ce61cd873fb2d83f5af7366"
"\x04\b{\x06I\"\x10_csrf_token\x06:\x06EFI\"16Jd0yiVSYGA57bEkV3rLDScxLqjLfIXG1rxlyf5GZbQ=\x06;\x00F"
127.0.0.1:6379>

Ở đây 5bc8c00a1ce61cd873fb2d83f5af7366 chính là Session ID và \x04\b{\x06I\”\x10_csrf_token\x06:\x06EFI\”16Jd0yiVSYGA57bEkV3rLDScxLqjLfIXG1rxlyf5GZbQ=\x06;\x00F chính là session data đã được mã hóa.

Tài liệu tham khảo