Trong phần một của loạt bài này, chúng tôi đã giới thiệu Devise bằng cách sử dụng một ứng dụng mẫu để khám phá các mô-đun, trình trợ giúp, chế độ xem, bộ điều khiển và tuyến đường.
Trong phần này, chúng ta sẽ khám phá cách sử dụng Devise nâng cao hơn, cụ thể là cách sử dụng OmniAuth, xác thực API và Authtrail.
Hãy đi thẳng vào vấn đề!
Xác thực bằng OmniAuth cho Ruby
Ngày nay, hầu hết mọi ứng dụng web bạn gặp đều sẽ cung cấp cho bạn tùy chọn đăng nhập thông qua nhiều nhà cung cấp dịch vụ xác thực, từ các mạng xã hội như Twitter và Facebook cho đến Google, GitHub, v.v.
Trong nhiều trường hợp, xác thực đa nhà cung cấp tiện lợi này được hỗ trợ bởi thư viện có tên OmniAuth. OmniAuth là thư viện xác thực linh hoạt và mạnh mẽ dành cho Ruby, cho phép bạn tích hợp với nhiều nhà cung cấp bên ngoài.
Nó cung cấp API đơn giản và thống nhất để kết nối với nhiều nhà cung cấp OAuth khác nhau. OmniAuth đặc biệt hữu ích trong những trường hợp bạn muốn cung cấp cho người dùng tùy chọn đăng ký hoặc đăng nhập bằng tài khoản mạng xã hội của họ. Với OmniAuth, bạn có thể dễ dàng thêm chức năng đăng nhập mạng xã hội vào ứng dụng Rails của mình.
Khi OmniAuth được sử dụng cùng với đá quý Devise, việc quản lý xác thực và ủy quyền người dùng càng trở nên dễ dàng hơn. Bạn có thể tận dụng các tính năng xác thực tích hợp của Devise trong khi sử dụng OmniAuth cho các tùy chọn đăng nhập của nhà cung cấp bên ngoài.
Bắt đầu với OmniAuth và Devise
Như đã đề cập, OmniAuth cho phép bạn tích hợp với một số nhà cung cấp dịch vụ xác thực bên thứ ba. Vì mục đích của bài viết này, chúng tôi sẽ sử dụng GitHub.
Cài đặt đá quý OmniAuth
Trong Gemfile của ứng dụng của bạn , thêm các dòng sau:
Và nếu bạn đang sử dụng đá quý OmniAuth 2.0+, bạn cũng cần thêm:
omniauth-rails_csrf_protection gem đảm bảo rằng mọi GET yêu cầu đối với luồng OAuth bị vô hiệu hóa. Nó cũng chèn trình xác minh mã thông báo Rails CSRF trước giai đoạn yêu cầu OAuth. Hai hành động này nhằm giảm thiểu các cuộc tấn công giả mạo trên nhiều trang web nhằm vào luồng xác thực OAuth.
Tiếp theo, chạy bundle install để cài đặt đá quý.
Tạo ứng dụng GitHub OAuth mới
Bây giờ chúng ta cần tạo một ứng dụng OAuth mới trên GitHub. Ứng dụng này sẽ hoạt động như một người dùng với quyền xác thực có thể dễ dàng thu hồi nếu cần.
Bước đầu tiên là truy cập trang cài đặt trong hồ sơ tài khoản GitHub của bạn. Sau đó, trên menu bên trái, nhấp vào "Cài đặt dành cho nhà phát triển". Bạn sẽ thấy một màn hình giống như màn hình bên dưới, nơi bạn có thể tạo ứng dụng OAuth mới:

Khi bạn nhấp vào "Đăng ký ứng dụng mới", bạn sẽ nhận được một màn hình như thế này:

Điền thông tin chi tiết vào biểu mẫu như sau:
- Tên ứng dụng - Đặt tên thích hợp cho ứng dụng OAuth mới của bạn.
- url trang chủ - Hiện tại, hãy sử dụng
http://localhost:3000/. Trong quá trình sản xuất, bạn sẽ sử dụng url trang chủ thực tế của ứng dụng. - Mô tả ứng dụng - Không cần thiết, nhưng bạn vẫn có thể có một cái nếu bạn có nhiều ứng dụng và cần phân biệt giữa chúng.
- url gọi lại ủy quyền - Đây là thông tin bắt buộc phải nhập và thường sẽ tuân theo định dạng url gọi lại OAuth như
http://<app-url>/users/auth/<application-provider>/callback. Điều đó có nghĩa là một số nhà cung cấp OAuth như Google có thể không tuân theo định dạng này, vì vậy bạn cần lưu ý điều đó.
Khi đã xong, hãy nhấp vào "Đăng ký ứng dụng". Trong màn hình tiếp theo, hãy tạo bí mật ứng dụng mới và ghi lại nó ở nơi nào đó an toàn (vì nó sẽ chỉ được hiển thị cho bạn một lần).
Định cấu hình Bộ khởi tạo Devise
Mở trình khởi tạo Devise config/initializers/devise.rb và điều hướng đến phần OmniAuth dành riêng cho GitHub. Nó có thể sẽ bị nhận xét, vì vậy hãy bỏ ghi chú và chỉnh sửa nó bằng ID và bí mật của ứng dụng GitHub OAuth mới của bạn:
Tạo Bộ điều khiển gọi lại OmniAuth
Nếu bạn đã tạo bộ điều khiển Devise, bạn sẽ tìm thấy OmniauthCallbacksController sẵn sàng để bạn tùy chỉnh cho phù hợp. Nếu của bạn không có ở đó, chỉ cần tạo một cái theo cách thủ công và chỉnh sửa nó như sau:
Một số điều cần lưu ý về đoạn mã trên:
from_omniauthlà một phương pháp chúng tôi sẽ triển khai bên trongUsercủa mình người mẫu.sign_in_and_redirectlà một phương thức bên trong OAuth.
Thêm di chuyển để sửa đổi mô hình người dùng
Bây giờ chúng ta cần thêm một số cột vào model User, cụ thể là provider cột và một uid cột:
Chạy bundle exec rails db:migrate để hoàn tất bước này.
Tạo mô hình có thể xác thực được toàn thể
Ở đây, chúng ta cần chỉnh sửa User mô hình bằng cách thêm mô-đun Devise Omniauthable vào nó:
Sau đó, thêm from_omni_auth phương pháp. Điều này sẽ được gọi từ Users::OmniauthCallbacksController mà chúng tôi vừa thiết lập:
Bây giờ chúng ta chỉ còn một việc nữa:thêm các liên kết đăng nhập vào chế độ xem Devise.
Thiết lập liên kết đăng nhập
Theo mặc định, Devise sẽ tự động thêm liên kết đăng nhập của nhà cung cấp thích hợp cho bạn trong chế độ xem đăng ký và đăng nhập của người dùng. Liên kết này sẽ sử dụng GET nhưng chúng tôi biết rằng OmniAuth 2.0+ ưu tiên POST yêu cầu. Vì vậy, chúng ta cần vô hiệu hóa liên kết và chèn liên kết của riêng mình bằng cách sử dụng POST yêu cầu:
Với điều đó, chúng tôi đã thiết lập thành công ứng dụng Ruby on Rails 7 với xác thực Devise và GitHub OAuth. Bạn có thể lấy mã nguồn đầy đủ của ứng dụng đồng hành tại đây.
Tiếp theo, chúng ta sẽ đi vào một trường hợp sử dụng nâng cao khác:sử dụng Devise để xác thực lệnh gọi API.
Xác thực API bằng Devise cho Ruby
Ngày nay, không có gì lạ khi người dùng mong đợi có thể kết nối với ứng dụng của bạn thông qua API. Trong phần này, chúng ta sẽ xem cách chúng ta có thể xác thực những yêu cầu đó của người dùng một cách an toàn bằng cách sử dụng Devise.
Trong trường hợp xác thực dựa trên trình duyệt thường dựa trên cookie thì hầu hết xác thực API diễn ra thông qua các mã thông báo được gọi là Mã thông báo Web JSON (hoặc đơn giản là JWT), được chuyển trong tiêu đề.
Mẹo :Với mục đích của phần này, chúng tôi giả định rằng chúng tôi đang làm việc với ứng dụng chỉ dành cho API Rails. Để làm theo, hãy tạo một cái với rails new app_name --api
Luồng xác thực dựa trên JWT
Như chúng tôi đã đề cập, xác thực API dựa trên mã thông báo JWT và điều quan trọng là chúng tôi hiểu cách diễn ra luồng xác thực dựa trên JWT. Về cơ bản, nó tuân theo một trình tự chung các bước như được nêu dưới đây:
- Khách hàng người dùng thực hiện lệnh gọi đến ứng dụng API.
- Ứng dụng API phản hồi bằng Mã thông báo web JSON (JWT), một mã thông báo xác thực có thể được sử dụng thay cho cookie.
- Các yêu cầu tiếp theo của khách hàng người dùng được thực hiện bằng cách sử dụng mã thông báo này trong
Authorizationtiêu đề. - Sau đó, người dùng có thể thực hiện hành động 'hủy phiên' của Devise, dẫn đến việc hủy mã thông báo và người dùng bị đăng xuất.
Bây giờ, hãy đưa luồng này vào hoạt động, bắt đầu với cái được gọi là chia sẻ tài nguyên giữa các nguồn gốc (CORS).
Thiết lập CORS
CORS thiết lập ứng dụng API của chúng tôi để cho phép các yêu cầu từ các nguồn bên ngoài. CORS là một chính sách bảo mật dựa trên HTTP xác định cách ứng dụng của bạn xử lý các yêu cầu bên ngoài. Theo mặc định, CORS sẽ chặn mọi yêu cầu bắt nguồn từ một miền khác với miền đã đưa ra yêu cầu ban đầu (nói cách khác, yêu cầu đến từ một "nguồn gốc" khác).
Để xử lý CORS đúng cách, chúng tôi sẽ sử dụng gem tiện lợi rack-cors . Trong Gemfile , bỏ ghi chú dòng bên dưới rồi chạy bundle install :
Ngoài ra, hãy mở tệp khởi tạo CORS tương ứng và sửa đổi nó như sau:
Một số lưu ý quan trọng về những gì chúng ta vừa thực hiện:
origins "*"- Đơn giản có nghĩa là ứng dụng API của chúng tôi hiện có thể nhận yêu cầu từ bất kỳ nguồn nào khác.expose: %w[Authorization Uid]- Theo mặc định làrack-corsgem không hiển thị tiêu đề ủy quyền và UID, nhưng chúng tôi cần chúng vì chúng tôi sẽ chuyển mã thông báo ủy quyền thông qua.
Sau khi hoàn tất, hãy cài đặt Devise và gem Devise-JWT đi kèm.
Thêm đá quý Devise và Devise-JWT vào ứng dụng Rails của bạn
devise-jwt gem là một phần mở rộng của Devise cho phép chúng tôi làm việc với mã thông báo JWT. Thêm đá quý vào Gemfile , sau đó chạy bundle install :
Chạy trình tạo cài đặt Devise bundle exec rails g devise:install .
Tạo và định cấu hình mô hình
Chúng ta cần thiết lập hai mô hình:mô hình người dùng Devise thông thường (bundle exec rails g devise User ) và mô hình mà chúng tôi sẽ sử dụng cho chiến lược thu hồi (nói cách khác, cách người dùng sẽ đăng xuất khỏi API):
Chúng tôi sửa đổi mô hình người dùng Devise thông thường để xác thực API bằng cách thêm mô-đun xác thực mã thông báo JWT và xác định chiến lược thu hồi mã thông báo để sử dụng mô hình thứ hai của chúng tôi, JwtDenylist :
Tiếp theo, chúng tôi định cấu hình mô hình thứ hai bằng cách tham chiếu chiến lược thu hồi và bảng thu hồi sẽ được sử dụng:
Trong phần sau, chúng tôi sẽ phác thảo việc thu hồi mã thông báo và lý do chúng tôi cần nó.
Tầm quan trọng của việc thu hồi mã thông báo
Tại sao việc thu hồi token lại quan trọng? Bởi vì mã thông báo JWT không có trạng thái. Máy chủ không biết gì về chúng ngoài việc ký chúng. Trong trường hợp như vậy, máy chủ không có cách nào để đăng xuất người dùng bằng cách thu hồi mã thông báo tương ứng. Vì không có cách nào để thu hồi từng mã thông báo riêng lẻ nên chúng tôi cần tạo một mã thông báo và yêu cầu máy chủ sử dụng mã thông báo đó.
Khi thu hồi mã thông báo, điều thực sự xảy ra là một phần duy nhất của mã thông báo, jti (JWT ID), được trích xuất và sử dụng theo chiến lược thu hồi đã xác định.
Tất nhiên, điều này đặt ra một câu hỏi khác về chiến lược thu hồi mã thông báo là gì. Tóm lại, đó là định nghĩa về cách máy chủ của bạn xử lý việc thu hồi mã thông báo. Có ba chiến lược thu hồi cơ bản:
- Chiến lược JTIMatcher - Ở đây, một cột duy nhất có tên "jti" được thêm vào mô hình người dùng, cột này cũng đóng vai trò là bảng thu hồi. Bất cứ khi nào người dùng đưa ra yêu cầu,
jtitrong tiêu đề được so khớp với các mã thông báo được lưu trữ và chỉ được phép truy cập khi tìm thấy kết quả khớp. - Chiến lược từ chối danh sách - Đối với cái này là
jtivà mã thông báo hết hạn (expsố mã thông báo bị thu hồi) được lưu trữ trong bảng cơ sở dữ liệu. Đối với mọi yêu cầu do người dùng thực hiện, một cuộc kiểm tra sẽ được thực hiện đối với bảng này để so sánh mã thông báo hiện tại của người dùngjtichống lại những cái bị thu hồi trong cơ sở dữ liệu. Nếu tìm thấy kết quả trùng khớp, yêu cầu của người dùng đó sẽ bị từ chối. - Chiến lược danh sách cho phép - Về mặt nào đó, chiến lược này tương tự như chiến lược đầu tiên, ngoại trừ việc ở đây bảng lưu trữ ID JWT có mối quan hệ một-nhiều với một bảng khác lưu trữ mã thông báo người dùng. Bất cứ khi nào có yêu cầu được thực hiện,
jticủa người dùng được lưu trữ trong bảng Danh sách cho phép được đối sánh với những gì được lưu trữ trong bảng đối sánh bằng mã thông báo. Quyền truy cập chỉ được phép nếu tìm thấy kết quả phù hợp.
Rõ ràng, đây là thông tin tổng quan rất đơn giản về việc thu hồi mã thông báo và bạn có thể tìm hiểu thêm về vấn đề này tại đây.
Thiết lập khóa ký cho mã thông báo JWT
Vì chúng tôi sẽ sử dụng mã thông báo an toàn để xác thực người dùng và yêu cầu của họ nên chúng tôi cần một cách để ký chúng. Đây là nơi khóa bí mật của chúng tôi xuất hiện. Bạn nên tạo một khóa mới khác với khóa bí mật Rails, secret_key_base .
Chạy bundle exec rake secret để tạo một khóa duy nhất, sau đó chỉnh sửa trình khởi tạo Devise để bao gồm khóa này:
Cuối cùng, hãy thiết lập bộ điều khiển.
Thiết lập bộ điều khiển
Bước cuối cùng để triển khai xác thực Devise cho API là thiết lập bộ điều khiển của chúng tôi. Để đơn giản, chúng tôi sẽ thiết lập hai bộ điều khiển:một để xử lý việc đăng ký và một cho các phiên.
Hãy tạo những thứ này theo cách thủ công, bắt đầu với bộ điều khiển đăng ký:
Đây là những gì đang xảy ra với bộ điều khiển này:
- Chúng tôi định cấu hình nó để đáp ứng các yêu cầu bằng JSON.
- Chúng tôi chỉ định
respond_withhành động trả về kết quả đăng ký thành công hay thất bại.
Và bây giờ là bộ điều khiển phiên:
Giống như bộ điều khiển đăng ký, ở đây chúng tôi chỉ định rằng bộ điều khiển sẽ phản hồi bằng JSON. Chúng tôi cũng xác định respond_with hành động khi người dùng đăng nhập thành công và respond_to_on_destroy để xử lý việc người dùng đăng xuất.
Và cùng với đó, bạn sẽ có luồng xác thực API hoạt động được hỗ trợ bởi mã thông báo Devise và JWT!
Phần cuối cùng của chúng tôi sẽ xem nhanh cách bạn có thể theo dõi thông tin đăng nhập của người dùng bằng Devise và Authtrail.
Theo dõi thông tin đăng nhập bằng Authtrail
Giả sử bạn muốn gửi cho người dùng ứng dụng của mình một email thông báo bất cứ khi nào ai đó đăng nhập vào tài khoản của họ, kèm theo các chi tiết như địa chỉ IP và dấu thời gian đăng nhập. Làm thế nào bạn có thể thực hiện được điều này?
Bạn sẽ cần theo dõi thông tin đăng nhập của người dùng, sau đó sử dụng thông tin đó trong email thông báo mà bạn gửi cho người dùng của mình. Nhưng trước tiên, bạn sẽ cần một cách theo dõi thông tin đăng nhập của người dùng. Điều này có thể được thực hiện bằng cách sử dụng một loại đá quý tiện lợi có tên là Authtrail, loại đá quý này cũng kết hợp tốt với Devise.
Cài đặt Authtrail
Bước đầu tiên là cài đặt gem bằng bundle add authtrail . Ngoài ra, vì bạn sẽ lưu trữ thông tin nhận dạng người dùng như email và địa chỉ IP trong cơ sở dữ liệu ứng dụng của mình nên bạn nên mã hóa dữ liệu này trong quá trình sản xuất bằng cách sử dụng kết hợp đá quý Lockbox và Blindindex.
Tiếp theo, chạy trình tạo Authtrail để tạo trình khởi tạo và di chuyển bảng đi kèm sẽ lưu trữ dữ liệu đăng nhập:
Cách thức hoạt động của Authtrail
Bất cứ khi nào người dùng cố gắng đăng nhập, một bản ghi Authtrail mới sẽ được tạo với các chi tiết quan trọng sau:
- Địa chỉ email đăng nhập được sử dụng
- Việc đăng nhập có thành công hay không
- Lý do đăng nhập không thành công (nếu đăng nhập thất bại)
- Địa chỉ IP của người dùng,
referrer, và nhiều hơn thế nữa
Sau đó, bạn có thể sử dụng thông tin này theo ý muốn. Ví dụ:bạn có thể gửi email thông báo cho người dùng, bao gồm thông tin email và địa chỉ IP, để cho họ biết rằng tài khoản của họ đã được thực hiện đăng nhập.
Xem qua tài liệu của Authtrail để xem tất cả các khả năng có sẵn cho bạn.
Kết thúc
Trong loạt bài này, chúng ta đã tìm hiểu sâu về viên ngọc Devise.
Đầu tiên, chúng ta phải nắm bắt được những điều cơ bản về Devise, bao gồm cách hoạt động của các mô-đun, trình trợ giúp, chế độ xem, bộ điều khiển và tuyến đường. Trong phần thứ hai và cũng là phần cuối cùng này, chúng ta đã khám phá cách sử dụng Devise với OAuth, Authtrail và để xác thực API.
Hy vọng rằng loạt bài này sẽ đóng vai trò là hướng dẫn hữu ích cho mọi vấn đề Phát triển xác thực.
Chúc bạn viết mã vui vẻ!
Tái bút. Nếu bạn muốn đọc các bài đăng của Ruby Magic ngay khi chúng được đăng tải, hãy đăng ký nhận bản tin Ruby Magic của chúng tôi và không bao giờ bỏ lỡ một bài đăng nào!