Cơ Sở Triển Khai Tính Năng Lịch Sử Mật Khẩu
Devise là giải pháp xác thực tiêu chuẩn trong hệ sinh thái Ruby on Rails, nhưng thiếu tính năng kiểm tra lịch sử mật khẩu theo mặc định. Việc bổ sung chức năng này giúp ngăn người dùng tái sử dụng mật khẩu cũ, giảm thiểu rủi ro bảo mật khi mật khẩu bị rò rỉ hoặc tấn công brute-force.
Thiết Kế Cơ Sở Dữ Liệu
Tạo bảng lưu trữ lịch sử mật khẩu với cấu trúc tối ưu:
# db/migrate/add_password_audit_trail.rb
class AddPasswordAuditTrail < ActiveRecord::Migration[7.0]
def change
create_table :password_audits do |t|
t.belongs_to :account, null: false, index: true
t.string :hashed_value, null: false
t.timestamp :archived_at, null: false
t.index [:account_id, :archived_at], name: "idx_audit_by_account"
end
end
end
Định nghĩa model tương ứng:
# app/models/password_audit.rb
class PasswordAudit < ApplicationRecord
belongs_to :account
self.table_name = "password_audits"
end
Cơ Chế Theo Dõi Mật Khẩu
Thực hiện ghi nhận và kiểm tra mật khẩu trong model người dùng:
# app/models/account.rb
class Account < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable
has_many :password_audits, dependent: :destroy
PASSWORD_HISTORY_LIMIT = 5
before_save :record_previous_password, if: :will_save_change_to_encrypted_password?
validate :ensure_password_freshness, if: :password_changed?
private
def record_previous_password
password_audits.create(
hashed_value: encrypted_password_was,
archived_at: Time.current
)
trim_audit_records
end
def trim_audit_records
ordered = password_audits.order(archived_at: :desc)
ordered.offset(PASSWORD_HISTORY_LIMIT).destroy_all
end
def ensure_password_freshness
return if password.blank?
new_hash = Devise::Encryptor.digest(Account, password)
if password_audits.exists?(hashed_value: new_hash)
errors.add(:password, "Mật khẩu đã được sử dụng gần đây. Vui lòng chọn mật khẩu mới.")
end
end
end
Tối Ưu Hóa Hiệu Năng
Thiết lập chỉ mục và giới hạn lịch sử để tránh vấn đề hiệu năng:
# config/initializers/devise_security.rb
Devise.setup do |config|
config.password_history_depth = 5
end
Trong model, sử dụng cấu hình này để kiểm soát số lượng bản ghi:
def trim_audit_records
max_records = Devise.password_history_depth || 5
password_audits.order(archived_at: :desc).offset(max_records).destroy_all
end
Kiểm Thử Tính Năng
Đảm bảo tính năng hoạt động chính xác qua các test case:
# test/models/account_test.rb
require "test_helper"
class AccountTest < ActiveSupport::TestCase
test "should reject recent passwords" do
user = Account.create!(email: "test@example.com", password: "OldPass123!")
user.update!(password: "NewPass456!")
assert user.valid?
user.password = "OldPass123!"
assert_not user.valid?
assert_includes user.errors[:password], "Mật khẩu đã được sử dụng gần đây"
end
end
Xử Lý Ngoại Lệ
Đảm bảo tương thích với chức năng reset mật khẩu:
def reset_password_token=(token)
super
@password_reset_triggered = true
end
def update_password(new_password, new_password_confirmation)
result = super
record_previous_password if result && @password_reset_triggered
result
end