Advertisement
  1. Code
  2. Ruby

Cách sử dụng Omniauth để xác thực người dùng

Scroll to top
Read Time: 10 min

() 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".

Facebook New AppFacebook New AppFacebook 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.

Facebook SecretFacebook SecretFacebook Secret

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ó nameemail. 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 uidprovider. 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à nameemail 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 provideruid đó 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 provideruid 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 UserAuthorization. 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é.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.