Áp Dụng ReCaptcha vào hệ thống sử dụng Ruby On Rails

Chắc các bạn cũng không xa lạ gì với phương pháp xác thực người dùng trên mạng Internet phổ biến nhất là phương pháp dùng captcha. Đây là phương pháp tạo ra đoạn kí tự với phần chữ bị làm méo đi nhưng vẫn đảm bảo con người có thể đọc ra được mà các công cụ tự động không nhận ra được.

alt

Thường thì các phương pháp xác thực này hay được dùng để xác thực các hành động trên trinh duyệt là người dùng thực sự chứ không phải là các con bot tự động (như đăng kí tài khoản, khảo sát ng dùng, vote sự kiện nào đó ...). Và gần đây Google đã phát triển 1 các kiểm tra mới khá là thú vị, ví dụ:

alt

Theo như google bảo thì hệ thống này sẽ dựa việc di chuyển con chuột trước khi phần tick check verify được click. Việc này đảm bảo được sự kiện này ko phải là action được gọi từ hàm javascript nào đó mà là do tương tác người dùng. Ngoài ra hệ thống này còn dựa trên địa chỉ IP, cookies đang dùng để kiểm tra xác thực người dùng.

Logic hoạt động về phía người dùng cũng rất đơn giản và trực quan. Cho lần Đầu tiên xác thực, người dùng chỉ việc click vào ô xác thực là thành công mà không cần phải làm bất cứ bài kiểm tra nào (a free pass). Tiếp sau đó khi người dùng cần xác thực thì sẽ phải hoàn thành 1 bài kiểm tra (tựa tựa như viết captcha nhưng thú vị hơn nhiều). Đó là google sẽ đưa ra 1 danh sách các ảnh và người dùng sẽ phải lựa chọn ra các ảnh phù hợp với nội dung câu hỏi đề ra:

Ví dụ

alt

Như trên ví dụ trên ở ảnh đầu tiên bạn sẽ phải tìm tất cả các ảnh có con gà trong ảnh để xác thực. Tương tự ở ví dụ 2 là bạn phải lấy hết ảnh mà có con mèo góp mặt vào, khá là dễ thương và không còn nhàm chán như gõ mấy đoạn text như kiểu gõ captcha thông thường.

Chắc hẳn là bạn cảm thấy rất hứng thú với kiểu xác thực này phải không nào 😄. Okie, ngay sau đây mình sẽ hướng dẫn bạn có thể áp dụng phương pháp này vào hệ thống của mình trên framework Ruby On Rails

Đầu tiên các bạn cần khai báo thêm gem "recaptcha" vào thư viện của app

# Gemfile
gem "recaptcha", require: "recaptcha/rails"
gem 'dotenv-rails', :require => 'dotenv/rails-now'

ở đây mình có khai báo thêm gem "dotenv-rails" dùng để lưu các giá trị KEY vào 1 file môi trường nhằm tránh bị lộ( mình sẽ nói rõ hơn ở phần dưới)

Tiếp theo bạn cần khai báo key để dùng recaptcha này

# config/initializers/recaptcha.rb
Recaptcha.configure do |config|
  config.public_key  = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
  config.private_key = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
  # Uncomment the following line if you are using a proxy server:
  # config.proxy = 'http://myproxy.com.au:8080'
end

vì recaptcha này do google phát triển và dữ liệu được lấy ra thông qua api của google, nên bạn cần dăng ký với google để lấy public/private key để dùng để đăng ký bạn vào link sau, làm theo hướng dẫn của google. Sau khi đăng ký 1 địa chỉ website của bạn thì google sẽ cho bạn 1 cặp key để xài dịch vụ recaptcha ứng với website đó ví dụ

Tuy nhiên đây là file cấu hình nên sẽ được lưu trong code hệ thống và nếu chẳng may code của bạn bị lộ thì có nghĩa là bạn sẽ bị lộ luôn cặp google key này. Do đó ở đây chúng ta sẽ dùng đến gem "dotenv-rails" mà mình đã nói ở trên để lưu riêng các key/value mà bạn muốn bảo mật và ko được upload cùng với code.

Để sử dụng, trước hết bạn cần xóa hết dữ liệu mà bạn đã thêm vào file "config/initializers/recaptcha.rb" ở trên. Sau đó tạo file ".env"

# .env
RECAPTCHA_PUBLIC_KEY = '<YOUR PUBLIC KEY>'
RECAPTCHA_PRIVATE_KEY = '<YOUR PRIVATE KEY>'

bạn không cần khai báo lại cặp key này trong file config, vì gem "recaptcha" đã tự load biến RECAPTCHA_PUBLIC_KEY và RECAPTCHA_PRIVATE_KEY để sử dụng làm key.

Okie, vậy là xong phần, cài đặt và cấu hình, tiếp theo ta sẽ phải khai báo ở view và xử lý trên controller

Ở view, trong form submit bạn cần thêm đoạn code <%= recaptcha_tags %> <!-- THIS -->

ví dụ

<%= form_for @user do |f| %>
  # ... other tags
  <%= recaptcha_tags %>
  <%= f.submit, "Submit" %>

<% end %>

ở đây khi form hiện ra sẽ xuất hiện thêm 1 đoạn check veryfi human trước nút submit, ngoài ra bạn có thể đặt ở bất kì vị trí nào bạn muốn ứng với vị trí đoạn code recaptcha_tags

Sau khi đã khai báo check ở view, việc tiếp theo ta cần làm là kiểm tra ở controller xem người dùng đã hoàn thành bài kiểm tra hay chưa

Ví dụ ở controller tạo user

# app/controllers/users_controller.rb
@user = User.new(params[:user].permit(:name))
if verify_recaptcha(model: @user) && @user.save
  redirect_to @user
else
  render 'new'
end

như bạn đã thấy đoạn code verify_recaptcha(model: @user) dùng để xác thực action người dùng truyền lên có đúng hay không. Nếu đúng và người dùng đăng ký thành công thì sẽ được chuyển tới trang info, còn nếu không thì sẽ hiện lại form để người dùng nhập tiếp.

Như vậy là các bạn đã áp dụng thành công việc sử dụng recaptcha trên form và xử lý logic trên controller rồi, rất đơn giản đúng không nào 😄. OK, tuy nhiên có 1 vấn đề nảy sinh, đó là với các controller và view được render sẵn rồi thì sao. Ví dụ bạn dùng gem devise để signin/signup user, và bạn muốn check captcha lúc đăng ký user mới thì phải làm sao bởi lẽ form và controller xử lý việc này được devise tự động tạo ra và xử lý trong gem. OK, các bạn đừng lo vì mình sẽ hướng dẫn giải pháp cho vấn đề này ngay sau đây thôi.

Giả sử bạn dùng model User.

  • Đầu tiên bạn check trong form đăng ký user mới (thường sẽ là file app/view/devise/sesions/new.html.erb) thêm dòng <%= recaptcha_tags %> vào vị trí bạn muốn hiện ô check captcha
  • Sau đó ta cần kiểm tra captcha trên controller lúc tạo user (thường sẽ là controller app/sessions_controller.rb)
class SessionsController < Devise::SessionsController
  prepend_before_action :check_captcha, only: [:create] # Change this to be any actions you want to protect.

  def create
    super
  end

  private
  def check_captcha
    unless verify_recaptcha
      self.resource = resource_class.new sign_up_params
      respond_with_navigational(resource) { render :new }
    end
  end
end

ở đây chúng ta đã thêm 2 đoạn code, đoạn đầu tiên là khai báo sẽ gọi hàm check_captcha trước khi chạy vào hàm tạo user, và đoạn code khai báo hàm check_captcha để xác định user truyền thông tin từ form lên đã hoàn thành bài check hay chưa. Nếu user chưa hoàn thành bài kiểm tra thì các lệnh trong điều kiện unless sẽ được thực thi và quay về màn hình đăng ký.

Tuy nhiên lúc này bạn sẽ không thấy thông báo lỗi gì đổ ra mà chỉ thấy hiện lại trang đăng ký, do đó ta cần sửa thêm 1 chút để cho thông báo lỗi hiện lên màn hình. Ta cần vào lại file viết form sign up (app/view/devise/sesions/new.html.erb) và thêm đoạn code sau vào vị trí bạn muốn xuất hiện lỗi

<div style="text-color:red;">
  <%= flash[:recaptcha_error] %> #in ra lỗi nếu có
</div>

bạn có thể sửa thêm css cho đoạn thông báo lỗi thêm đẹp hơn 😃.

Giải thích 1 chút ở đây, bình thường biến flash[:recaptcha_error] sẽ là nil, tuy nhiên nếu ta gọi hàm verify_recaptcha và kết quả là false thì gem recaptcha sẽ tự động gán text thông báo lỗi vào biến này, nên ở view bạn chỉ việc gọi và hiển thị lỗi thôi. Đồng thời nếu bạn muốn sử dụng riêng thông báo của mình thì bạn có thể gán riêng giá trị trong đoạn điều kiện unless vào 1 biến lỗi nào đó và gọi lại nó ở view.

Okie, giờ bạn hãy chạy đăng ký user lại và kiểm tra xem, mình tin là hệ thống đã chạy 'nuột nà' rồi đó 😉

Tham khảo:

All Rights Reserved