Việc tạo ra những token đặc biệt, an toàn và có thể chứa thông tin về mục đích sử dụng cũng như thời gian hết hạn của token rất hữu ích trong một số tình huống cụ thể. Bạn có thể tạo ra một token duy nhất cho các mục đích cụ thể như authenticate access_token, reset email hoặc reset password, sau đó gán chúng vào địa chỉ URL của ứng dụng của bạn và gửi cho người dùng qua email.
Sử dụng ActiveRecord:SignedId
Từ bây giờ bạn có thể sử dụng API ActiveRecord::SignedId
để tạo ra các token có thời hạn. Và bạn có thể dùng ActiveRecord để tìm bản ghi sử dụng signed id tương ứng
Hãy xem xét một tình huống trong đó chúng ta muốn gửi một liên kết đặt lại mật khẩu cho người dùng. Nếu bạn muốn tạo ra một token duy nhất có hiệu lực trong vòng 1 ngày.
user = User.first
signed_id = user.signed_id(expires_in: 1.day, purpose: :password_reset)
# Output signed_id
# eyJfcmFpbHMiOnsiZGF0YSI6ODg3LCJleHAiOiIyMDIzLTEwLTI1VDE2OjM2OjI4LjY1MloiLCJwdXIiOiJ1c2VyL3Bhc3N3b3JkX3Jlc2V0In19--4b1bde069dad4fa959fc111dbb401fac724cd43ab7aea772841fc90d87e42354
Mã signed_id
trên sẽ có thời hạn 1 ngày và mục đích (purpose) chúng ta có thể ghi bất kỳ thứ gì củng được. Sau đó để tìm lại record từ signed_id trên thì làm như sau:
User.find_signed(signed_id, purpose: :password_reset)
# Output
# <User:0x000000010ed60a38
Vì ở trên khi signed_id ta có điền purpose thì khi find chúng ta củng phải truyền lại purpose. Trường hợp không đúng thì kết quả sẽ trả về nil
, còn khi hết hạn sẽ raise lỗi
Sử dụng ActiveRecord::Base::generates_token_for
class User < ActiveRecord::Base
has_secure_password
generates_token_for :password_reset, expires_in: 1.day do
BCrypt::Password.new(password_digest).salt[-10..]
end
end
Khởi tạo token
user = User.first
token = user.generate_token_for(:password_reset)
# Output
# eyJfcmFpbHMiOnsiZGF0YSI6Wzg4Nywid3FxLlhwdWJjLiJdLCJleHAiOiIyMDIzLTEwLTI0VDE2OjU0OjA5LjU0MloiLCJwdXIiOiJVc2VyXG5wYXNzd29yZF9yZXNldFxuNjAifX0=--8430fc9ee974752f075c78c6d...
Tìm record từ token đã tạo ở trên
User.find_by_token_for(:password_reset, token)
# Output:
# <User:0x000000010ed60a38
generates_token_for
hoạt động tương tự như signed_id
. Trong khi signed_id
sử dụng model ID làm dữ liệu đầu vào, thì generates_token_for
sử dụng sự kết hợp giữa model ID và giá trị được trả về bởi block. Block này nên tham chiếu tới một thuộc tính liên quan đến mục đích của token (Ở đây là password_digest
). Khi thuộc tính này (password_digest) được cập nhật, dữ liệu đầu vào cũng được cập nhật và từ đó làm vô hiệu hóa token cũ.
Trong ví dụ trên, sự kết hợp dữ liệu đầu vào sẽ là model ID và 10 ký tự cuối cùng của Bycrypt Password
Khi bạn cập nhật mật khẩu, thì password_digest
sẽ thay đổi và do đó token bị vô hiệu hóa sau lần cập nhật đầu tiên của mật khẩu. Điều này khiến cho token chỉ có thể được sử dụng một lần.
Trước đây với bài toán reset password
hay reset email
thông thường ta sẽ tạo ra 2 cột để lưu trữ (reset_email_token và reset_email_token_expired). Bây giờ sẽ không cần nửa bởi vì đã có generates_token_for
Cảm ơn bạn đã đọc bài viết này về tính năng generates_token_for
và signed_id
trong Rails 7.1. Hy vọng rằng bạn đã tìm thấy thông tin hữu ích và có thêm kiến thức về cách sử dụng tính năng này để tạo ra các token đặc biệt và an toàn trong ứng dụng của mình. Nếu bạn có bất kỳ câu hỏi hoặc góp ý nào, đừng ngần ngại chia sẻ. Chúng tôi luôn đánh giá phản hồi từ độc giả. Hãy tiếp tục theo dõi và chia sẻ kiến thức về lĩnh vực phát triển ứng dụng web. Cảm ơn bạn đã ủng hộ!