() translation by (you can also view the original English article)
Tôi ghét phải đăng ký cho các website. Tôi đã từng đăng ký quá nhiều username cho nhiều website, dẫn dến những khó khăn khi phải nhớ những thông tin của tôi Ngày nay, đa số site đã bắt đầu đề xuất giải pháp thay thế để đăng ký, cho phép bạn sử dụng Facebook, Twitter hoặc thậm chí tài khoản Google của bạn. Việc tích hợp này đôi khi tạo cảm giác như một nhiệm vụ dài hơi và khó khăn. Nhưng đừng sợ, Omniauth sẽ giúp bạn.
Omniauth cho phép bạn dễ dàng tích hợp hơn các provider xác thực khác, gồm có Facebook, Google, Twitter và Github. Trong hướng dẫn này, tôi sẽ giải thích làm thế nào để tích hợp các provider xác thực này vào ứng dụng của bạn.
Bước 1: Chuẩn bị ứng dụng của bạn
Hãy tạo ứng dụng Rails mới và bổ sung các gem cần thiết. Tôi giả sử bạn đã cài Ruby và Ruby on Rails 3.1 bằng RubyGems.
1 |
|
2 |
rails new omniauth-tutorial |
Giờ mở Gemfile
của bạn và tham chiếu đến gem omniauth.
1 |
|
2 |
gem 'omniauth' |
Tiếp theo, như thông lệ, chạy lệnh bundle install
để cài đặt gem.
Bước 2: Tạo một provider
Để bổ sung provider cho Omniauth, bạn sẽ cần đăng ký làm developer trên trang của provider. Sau khi đăng ký, bạn sẽ nhận được 2 chuỗi (kiểu như username và password), bạn cần nhập nó vào Omniauth. Nếu bạn đang dùng OpenID, thì tất cả bạn cần là OpenID URL.
Nếu bạn muốn dùng xác thực của Facebook, hãy đến trang developers.facebook.com/apps và click vào "Create New App".



Hãy điến tất cả thông tin cần thiết, và khi đã hoàn thành, copy ID và Secret của App.



Cấu hình Twitter có hơi phức tạp trên máy phát triển, vì họ không cho phép bạn dùng localhost làm domain cho callback. Cấu hình môi trường phát triển của bạn không nằm trong phạm vi hướng dẫn này, tuy vậy, tôi đề nghị bạn sử dụng Pow nếu bạn sử dụng Mac.
Bước 3: Bổ sung Provider của bạn vào App
Tạo một file mới trong thư mục config/initializers
với tên gọi omniauth.rb
. Chúng ta sẽ cấu hình xác thực trong file này.
Paste đoạn code sau vào file chúng ta tạo ra trước đó:
1 |
|
2 |
Rails.application.config.middleware.use OmniAuth::Builder do |
3 |
provider :facebook, YOUR_APP_ID, YOUR_APP_SECRET |
4 |
end
|
Thật sự đây lá tất cả cấu hình bạn cần để hoàn thành nó. Phần còn lại do Omniauth đảm trách, chúng ta sẽ tìm hiểu trong bước tiếp theo.
Bước 4: Tạo trang đăng nhập
Hãy tạo controller sessions của chúng ta. Chạy code sau đây trong terminal để tạo ra controller sessions
, và hành động new
, create
, và failure
.
1 |
rails generate controller sessions new create failure |
Tiếp theo, mở file config/routes.rb
và thêm vào:
1 |
|
2 |
get '/login', :to => 'sessions#new', :as => :login |
3 |
match '/auth/:provider/callback', :to => 'sessions#create' |
4 |
match '/auth/failure', :to => 'sessions#failure' |
Hãy chia nhỏ file này ra:
- Dòng đầu tiên dùng để tạo form đăng nhập đơn giản, ở đó người dùng sẽ thấy liên kết "Connect with Facebook".
- Dòng thứ hai để đón callback từ provider. Sau khi người dùng xác thực ứng dụng, provider điều hướng người dùng về url này, vì vậy ta có thể sử dụng data của họ.
- Dòng sau cùng sẽ dùng khi có vấn đề, hoặc nếu người dùng không xác thực ứng dụng.
Bảo đảm rằng bạn xoá các route đã được tự động tạo ra khi chạy lệnh rails generate
. Chúng không cần thiết cho dự án nhỏ của chúng ta.
Mở file app/controllers/sessions_controller.rb
và tạo phương thức create
như sau:
1 |
|
2 |
def create |
3 |
auth_hash = request.env['omniauth.auth'] |
4 |
|
5 |
render :text => auth_hash.inspect |
6 |
end
|
Phương thức này để bảo đảm mọi thứ đang hoạt động. Duyệt localhost:3000/auth/facebook và bạn sẽ được điều hướng đến Facebook để có thể xác thực ứng dụng của bạn (tuyệt phải không?). Xác thực nó, và bạn sẽ được điều hướng trở về ứng dụng của bạn và thấy hash với vài thông tin. Ở giữa sẽ là tên bạn, user id của Facebook, và email của bạn, cùng những thứ khác.
Bước 5: Tạo Model User
Tiếp theo là tạo model user để nhiều người dùng có thể đăng ký bằng tài khoản Facebook của họ. Trong Rails console (rails console
) tạo một model mới.
1 |
rails generate model User name:string email:string |
Từ giờ, model
của user chỉ có name
và email
. Với chúng, ta cần một cách để nhận ra người dùng khi họ đăng nhập lần sau. Ghi nhớ rằng chúng ta không có field nào trong model của user cho mục đích này.
Ý tưởng sau ứng dụng mà chúng ta đang xây dựng là một người dùng có thể chọn giữa việc dùng Facebook hoặc Twitter (hoặc bất kỳ provider khác) để đăng ký, vì vậy chúng ta cần một model nữa để lưu thông tin.
1 |
rails generate model Authorization provider:string uid:string user_id:integer |
Một người dùng sẽ có hoặc nhiều lần xác thực, khi ai đó cố gắng đăng nhập bằng một provider, chúng ta chỉ cần xem các xác thực trong database và tìm kiếm thông tin nào khớp với trường uid
và provider
. Bằng cách này, ta cũng cho phép các người dùng có nhiều provider, để học có thể đăng nhập sau đó bằng Facebook, Twitter, hoặc bất kỳ provider khác mà họ đã cấu hình.
Bổ sung code sau vào file app/models/user.rb
.
1 |
|
2 |
has_many :authorizations |
3 |
validates :name, :email, :presence => true |
Điều này xác định rằng một user
có nhiều xác thực, và name
và email
trong cơ sở dữ liệu là cần thiết.
Tiếp theo, đến file app/modes/authorization.rb
, thêm vào:
1 |
|
2 |
belongs_to :user |
3 |
validates :provider, :uid, :presence => true |
Trong model này, chúng ta chỉ định rằng mỗi xác thực được gán cho một user
cụ thể. Đồng thời ta cũng thiết lập một số đánh giá.
Bước 6: Bổ sung một chút logic cho controller sessions
Hãy bổ sung code cho controller
sessions để nó ghi lại lịch sử đăng nhập (hoặc đăng ký) của user. Mở file app/controllers/sessions_controller.rb
và bổ sung phương thức create
như vầy:
1 |
|
2 |
def create |
3 |
auth_hash = request.env['omniauth.auth'] |
4 |
|
5 |
@authorization = Authorization.find_by_provider_and_uid(auth_hash["provider"], auth_hash["uid"]) |
6 |
if @authorization |
7 |
render :text => "Welcome back #{@authorization.user.name}! You have already signed up." |
8 |
else
|
9 |
user = User.new :name => auth_hash["user_info"]["name"], :email => auth_hash["user_info"]["email"] |
10 |
user.authorizations.build :provider => auth_hash["provider"], :uid => auth_hash["uid"] |
11 |
user.save |
12 |
|
13 |
render :text => "Hi #{user.name}! You've signed up." |
14 |
end
|
15 |
end
|
Code này thực sự cần refactor, nhưng ta sẽ xử lý việc này sau. Hãy xem nó trước:
- Chúng ta kiểm tra liệu một xác thực có tồn tại cho
provider
vàuid
đó chưa. Nếu có, chúng ta đón chào user quay trở lại. - Nếu xác thực không tồn tại, chúng ta đăng ký cho người dùng. Chúng ta tạo một người dùng mới với name và email do provider cung cấp (trường hợp này là Facebook), và chúng ta dựa vào xác thực với
provider
vàuid
ta nhận được.
Hãy test thử xem! Duyệt localhost:3000/auth/facebook và bạn sẽ thấy "You've signed up". Nếu bạn tải lại trang, giờ bạn sẽ thấy "Welcome back".
Bước 7: Cho phép nhiều provider
Kịch bản lý tưởng sẽ là cho phép user đăng ký bằng một provider, và sau đó bổ sung một provider khác để anh ta có thể có nhiều chọn lựa để đăng nhập. Hiện giờ, ứng dụng của chúng ta không làm được điều này. Chúng ta cần refactor code của chúng ta một chút. Hãy thay đổi phương thức create
trong sessions_controller.rb
thành như sau:
1 |
|
2 |
def create |
3 |
auth_hash = request.env['omniauth.auth'] |
4 |
|
5 |
if session[:user_id] |
6 |
# Means our user is signed in. Add the authorization to the user
|
7 |
User.find(session[:user_id]).add_provider(auth_hash) |
8 |
|
9 |
render :text => "You can now login using #{auth_hash["provider"].capitalize} too!" |
10 |
else
|
11 |
# Log him in or sign him up
|
12 |
auth = Authorization.find_or_create(auth_hash) |
13 |
|
14 |
# Create the session
|
15 |
session[:user_id] = auth.user.id |
16 |
|
17 |
render :text => "Welcome #{auth.user.name}!" |
18 |
end
|
19 |
end
|
Hãy xem lại:
- Nếu user đã đăng nhập, chúng ta sẽ bổ sung provider mà họ đang sử dụng cho tài khoản của họ.
- Nếu họ chưa đăng nhập, chúng ta sẽ thử tìm user với provider đó, hoặc tạo mới nếu cầu thiết.
Để code trên hoạt động, chúng ta cần bổ sung vài phương thức cho model User
và Authorization
. Mở user.rb
và bổ sung phương thức sau:
1 |
|
2 |
def add_provider(auth_hash) |
3 |
# Check if the provider already exists, so we don't add it twice
|
4 |
unless authorizations.find_by_provider_and_uid(auth_hash["provider"], auth_hash["uid"]) |
5 |
Authorization.create :user => self, :provider => auth_hash["provider"], :uid => auth_hash["uid"] |
6 |
end
|
7 |
end
|
Nếu user chưa có provider này liên kết với tài khoản của họ, chúng ta sẽ tiếp tục và bổ sung nó - đơn giản. Giờ bổ sung phương thức này vào file authorization.rb
:
1 |
|
2 |
def self.find_or_create(auth_hash) |
3 |
unless auth = find_by_provider_and_uid(auth_hash["provider"], auth_hash["uid"]) |
4 |
user = User.create :name => auth_hash["user_info"]["name"], :email => auth_hash["user_info"]["email"] |
5 |
auth = create :user => user, :provider => auth_hash["provider"], :uid => auth_hash["uid"] |
6 |
end
|
7 |
|
8 |
auth
|
9 |
end
|
Trong code bên trên, chúng cố gằng tìm một xác thực khớp với yêu cầu, và nếu không thành công, thì ta tạo ra một user mới.
Nếu bạn muốn thử ở máy nội bộ, bạn cần một provider thứ 2 cho xác thực. Bạn có thể dùng hệ thống Twitter's OAuth, nhưng tôi đã đề cập trước đó, bạn sẽ cần một phương pháp khác, vì Twitter không cho phép sử dụng "localhost" làm domain của callback (ít nhất là nó không dành cho tôi). Bạn cũng có thể thử host phần code của bạn trên Heroku, chọn lựa này hoàn hảo cho site đơn giản như chúng ta đang xây dựng.
Bước 8: Một số điều chỉnh bổ sung
Sau cùng, dĩ nhiên chúng ta cho phép người dùng đăng xuất. Bổ sung phần code này vào controller sessions:
1 |
|
2 |
def destroy |
3 |
session[:user_id] = nil |
4 |
render :text => "You've logged out!" |
5 |
end
|
Chúng ta cũng cần tạo route (routes.rb
).
1 |
|
2 |
get '/logout', :to => 'sessions#destroy' |
Đơn giản vậy thôi! Nếu bạn duyệt localhost:3000/logout, session của bạn sẽ được xoá đi, và bạn sẽ đăng xuất. Việc này khiến nó dễ dàng hơn khi thử nhiều tài khoản và nhà cung cấp. Chúng tôi cũng cần bổ sung một message để hiển thị khi người dùng từ chôi truy xuất vào ứng dụng của chúng ta. Nếu bạn nhớ lại, chúng ta đã bổ sung route này ở lúc đầu bài viết. Giờ ta chỉ cần bổ sung phương thức vào controller sessions
.
1 |
|
2 |
def failure |
3 |
render :text => "Sorry, but you didn't allow access to our app!" |
4 |
end
|
Cuối cùng nhưng cũng quan trọng không kém, hãy tạo trang login, ở trang này người dùng có thể click vào liên kết "Connect with Facebook". Mở app/views/sessions/new.html.erb
và thêm vào:
1 |
|
2 |
<%= link_to "Connect With Facebook", "/auth/facebook" %> |
Nếu bạn đến localhost:3000/login, bạn sẽ thấy một liên kết dẫn bạn đến trang xác thực của Facebook.
Tổng kết
Tôi hy vọng bài viết này đã cung cấp cho bạn một ví dụ ngắn gọn cách hoạt động của Omniauth. Nó được xem là một gem mạnh mẽ, và cho phép bạn tạo ra các website không cần người dùng phải đăng ký, đây luôn là một lợi thế. Bạn có thể tìm hiểu về Omniauth trên Github.
Nếu bạn có câu hỏi hãy cho tôi biết nhé.