Mục Lục
1. Session là gì?
Trong lĩnh vực phát triển ứng dụng web, session được hiểu là một phiên làm việc, nơi người dùng tương tác với ứng dụng. Session bắt đầu khi người dùng truy cập ứng dụng lần đầu tiên và kết thúc khi họ thoát ra. Mỗi session thường được gán một mã định danh duy nhất, gọi là Session ID, cùng với một tập hợp dữ liệu (hash) chứa thông tin liên quan đến người dùng. Ví dụ, session có thể lưu trữ định danh người dùng, lịch sử các bài viết đã xem, hoặc danh sách các sản phẩm mà người dùng quan tâm và có ý định mua.
Session ID
Session ID là một chuỗi ký tự ngẫu nhiên, đóng vai trò là dấu hiệu để phân biệt giữa các session khác nhau. Trong Rails, Session ID là một chuỗi 32 ký tự, được tạo ra bằng cách mã hóa MD5 từ một chuỗi ngẫu nhiên khác. Chuỗi ngẫu nhiên này được tạo thành từ nhiều yếu tố: thời gian hiện tại, một số ngẫu nhiên nằm giữa 0 và 1, ID của tiến trình Ruby và một chuỗi ký tự cố định.
Trong Rails 4, bạn có thể truy xuất giá trị của Session ID trong controller như sau:
pry(#)> session.id
=> "06383951600dd0fc8713fafd63142fce"
Session ID được lưu trữ ở phía client trong một cookie và được gửi kèm theo mỗi yêu cầu đến server. Tên của cookie này có thể được tùy chỉnh thông qua việc thiết lập giá trị cho biến key trong file config/initializers/session_store.rb.
SenTâyHồ::Application.config.session_store :cookie_store, key: '_my_key'
Khi người dùng truy cập vào website, họ có thể kiểm tra cookie của trình duyệt và sẽ thấy một cookie với tên _my_key (nội dung của cookie này sẽ được giải thích chi tiết hơn ở các phần sau).
2. Các phương thức lưu trữ Session Data
Bài viết này sẽ giới thiệu ba phương thức lưu trữ session data phổ biến trong Rails 4: CookieStore, ActiveRecordStore và RedisStore.
2.1 CookieStore
CookieStore là phương thức lưu trữ session data mặc định của Rails từ phiên bản Rails 2. Với CookieStore, không chỉ Session ID mà toàn bộ session data được lưu trữ trực tiếp trong cookie ở phía client. Điều này có nghĩa là server không cần lưu trữ bất kỳ thông tin nào liên quan đến session, mà chỉ cần đọc thông tin từ cookie mà client gửi kèm theo mỗi request. Ưu điểm của phương pháp này là cải thiện tốc độ ứng dụng web, tuy nhiên, nó cũng có nhược điểm là giới hạn về dung lượng lưu trữ, do cookies chỉ có thể chứa tối đa 4KB dữ liệu.
Để đảm bảo tính bảo mật, Rails mặc định sẽ mã hóa session data bằng ActiveSupport::MessageEncryptor trước khi gửi xuống trình duyệt. Quá trình mã hóa diễn ra như sau:
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)
encrypted_and_signed_data = encryptor.encrypt_and_sign session_data
Trong đó, SECRET_KEY_BASE là secret key được định nghĩa trong file config/secrets.rb, còn session_data là dữ liệu của session dưới dạng hash, có thể được truy xuất trong controller bằng cách:
session_data = session.to_hash
2.2 ActiveRecordStore
ActiveRecordStore sử dụng ActiveRecord để quản lý session, nghĩa là tất cả dữ liệu liên quan đến session sẽ được lưu trữ trong database, tương tự như các model khác trong ứng dụng web. Để sử dụng ActiveRecordStore, bạn cần thêm gem activerecord-session_store vào Gemfile và chạy lệnh bundle install:
gem 'activerecord-session_store'
Tiếp theo, tạo một file migration mới để tạo bảng lưu trữ dữ liệu session:
rails generate active_record:session_migration
Sau đó, chạy migration:
rake db:migrate
Lệnh này sẽ tạo một bảng mới trong database với tên là sessions, có cấu trúc như sau:
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 này thông qua model tương ứng trong Rails:
pry(main)> ActiveRecord::SessionStore::Session
=> ActiveRecord::SessionStore::Session(id: integer, session_id: string, data: text, created_at: datetime, updated_at: datetime)
Cần lưu ý rằng, khác với CookieStore, các phương thức lưu trữ khác như ActiveRecordStore và RedisStore không lưu trữ Session ID trong session data.
Tiếp theo, cấu hình session store trong file config/initializers/session_store.rb và khởi động lại server:
SenTâyHồ::Application.config.session_store :active_record_store, key: '_my_key'
Lúc này, khi kiểm tra lại cookie trên trình duyệt, bạn sẽ thấy chỉ còn Session ID được lưu trữ, thay vì toàn bộ session data như trong trường hợp của CookieStore.
Về cách thức mã hóa dữ liệu: ActiveRecordStore serialize dữ liệu bằng module Marshal, sau đó encode sang dạng base 64 và lưu vào database.
Base64.strict_encode64(Marshal.dump(session_data))
2.3 RedisStore
RedisStore có ý tưởng tương tự như ActiveRecordStore: Session ID được lưu trữ trong cookie ở phía client, còn session data được lưu trữ ở phía server. Tuy nhiên, thay vì sử dụng các cơ sở dữ liệu truyền thống như MySQL hay PostgreSQL, RedisStore sử dụng Redis – một cơ sở dữ liệu dạng key-value nổi tiếng với tốc độ đọc và ghi cực nhanh. Để sử dụng RedisStore, bạn cần thêm gem redis-rails vào Gemfile và chạy lệnh bundle install:
gem 'redis-rails'
Sau đó, cấu hình session store trong file config/initializers/session_store.rb và khởi động lại server:
SenTâyHồ::Application.config.session_store :redis_store, key: '_my_key'
RedisStore sử dụng phương pháp mã hóa dữ liệu đơn giản: nó 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ể kết nối đến database bằng lệnh redis-cli để xem các 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"
"x04b{x06I"x10_csrf_tokenx06:x06EFI"16Jd0yiVSYGA57bEkV3rLDScxLqjLfIXG1rxlyf5GZbQ=x06;x00F"
127.0.0.1:6379>
Trong ví dụ trên, 5bc8c00a1ce61cd873fb2d83f5af7366 là Session ID, còn x04b{x06I"x10_csrf_tokenx06:x06EFI"16Jd0yiVSYGA57bEkV3rLDScxLqjLfIXG1rxlyf5GZbQ=x06;x00F là session data đã được mã hóa.
