Bạn sẽ hy sinh một số bảo mật bằng cách thực hiện việc này, nhưng chắc chắn là có thể. Có hai cách bạn có thể thực hiện việc này.
Trước tiên, bạn có thể ghi đè phương thức make_token trong mô hình người dùng của mình. Mô hình hiện được thực hiện như sau.
def make_token
secure_digest(Time.now, (1..10).map{ rand.to_s })
end
Mỗi khi người dùng đăng nhập vào, có hoặc không có một cookie, phương pháp make_token
được gọi là tạo ra và tiết kiệm rất mới remember_token
cho người dùng. Nếu bạn có một số giá trị khác duy nhất cho người dùng không thể đoán được, bạn có thể thay thế phương thức make_token
.
def make_token
secure_digest(self.some_secret_constant_value)
end
Điều này sẽ đảm bảo rằng mã thông báo không bao giờ thay đổi, nhưng cũng sẽ cho phép bất kỳ ai có mã thông báo mạo danh người dùng.
Ngoài điều này, nếu bạn nhìn vào phương pháp handle_remember_cookie!
trong tệp authenticated_system.rb
, bạn sẽ có thể thay đổi phương pháp này để làm việc cho bạn.
def handle_remember_cookie!(new_cookie_flag)
return unless @current_<%= file_name %>
case
when valid_remember_cookie? then @current_<%= file_name %>.refresh_token # keeping same expiry date
when new_cookie_flag then @current_<%= file_name %>.remember_me
else @current_<%= file_name %>.forget_me
end
send_remember_cookie!
end
Bạn sẽ nhận thấy rằng phương pháp này gọi là ba phương pháp trong mô hình người dùng, refresh_token
, remember_me
, và forget_me
.
def remember_me
remember_me_for 2.weeks
end
def remember_me_for(time)
remember_me_until time.from_now.utc
end
def remember_me_until(time)
self.remember_token_expires_at = time
self.remember_token = self.class.make_token
save(false)
end
#
# Deletes the server-side record of the authentication token. The
# client-side (browser cookie) and server-side (this remember_token) must
# always be deleted together.
#
def forget_me
self.remember_token_expires_at = nil
self.remember_token = nil
save(false)
end
# refresh token (keeping same expires_at) if it exists
def refresh_token
if remember_token?
self.remember_token = self.class.make_token
save(false)
end
end
Cả ba phương pháp này đều đặt lại mã thông báo. forget_me
đặt thành nil
, trong khi hai thiết bị còn lại đặt giá trị trả về bằng make_token
. Bạn có thể ghi đè lên các phương thức này trong mô hình người dùng, để ngăn chúng đặt lại mã thông báo nếu nó tồn tại và không hết hạn. Đó có lẽ là cách tiếp cận tốt nhất, hoặc bạn có thể thêm một số logic bổ sung vào phương thức handle_remember_cookie!
, mặc dù điều đó có thể sẽ có nhiều công việc hơn.
Nếu tôi là bạn, tôi sẽ ghi đè remember_me_until
, forget_me
và refresh_token
trong mô hình người dùng. Sau đây sẽ làm việc.
def remember_me_until(time)
if remember_token?
# a token already exists and isn't expired, so don't bother resetting it
true
else
self.remember_token_expires_at = time
self.remember_token = self.class.make_token
save(false)
end
end
#
# Deletes the server-side record of the authentication token. The
# client-side (browser cookie) and server-side (this remember_token) must
# always be deleted together.
#
def forget_me
# another computer may be using the token, so don't throw it out
true
end
# refresh token (keeping same expires_at) if it exists
def refresh_token
if remember_token?
# don't change the token, so there is nothing to save
true
end
end
Lưu ý rằng bằng cách thực hiện việc này, bạn đang lấy ra các tính năng bảo vệ bạn khỏi bị đánh cắp mã thông báo. Nhưng đó là một quyết định về lợi ích chi phí mà bạn có thể thực hiện.
Cảm ơn một tấn! Vậy chức năng của người dùng đang kiểm tra "Nhớ thông tin đăng nhập của tôi" hoạt động như thế nào? Liệu nó có còn nhớ chúng trong khoảng thời gian được đặt trong phương thức 'remember_me' không? – Shpigford
Nó vẫn ghi nhớ chúng trong 2 tuần, như trong phương thức remember_me, nhưng 2 tuần đó bắt đầu lần đầu tiên mã thông báo được sử dụng. Nói cách khác, nếu bạn đăng nhập từ máy tính A, sau đó 10 ngày đăng nhập từ máy tính B, 4 ngày sau mã thông báo hết hạn trên cả hai máy tính. – jcnnghm
Tuyệt vời. Cảm ơn một lần nữa vì sự giúp đỡ của bạn! – Shpigford