Gem Devise - Rails
Khi bạn làm việc với ngôn ngữ lập trình yêu thích của mình, bạn thường tìm kiếm các công cụ để làm cho công việc của mình dễ dàng hơn. Trong Ruby, những công cụ này (gems) được tạo ra hàng ngày, nhưng chỉ một số trong số chúng là đủ tốt để chiếm được cảm tình của các coder. Gem devise là 1 ...
Khi bạn làm việc với ngôn ngữ lập trình yêu thích của mình, bạn thường tìm kiếm các công cụ để làm cho công việc của mình dễ dàng hơn. Trong Ruby, những công cụ này (gems) được tạo ra hàng ngày, nhưng chỉ một số trong số chúng là đủ tốt để chiếm được cảm tình của các coder. Gem devise là 1 trong những số đó. Nó được sử dụng rộng rãi hầu hết ở mọi project. Gem Devise là một giải pháp authentication linh hoạt cho Rails. Nó
- Dựa trên Rack
- Một solution MVC đầy đủ dựa trên Rails engines
- Cho phép nhiều models cùng đăng nhập tại một thời điểm
- Làm việc dựa trên module: Chỉ sử dụng những gì bạn thực sự cần. Nó chạy những module độc lập và riêng biệt
Cài đặt trên Rails:
Giả sử ta muốn cài devise cho 1 model tên là User
echo "gem 'devise'" >> Gemfile # Hoặc sửa Gemfile thêm dòng: gem 'devise' bundle install # Cài đặt gem rails generate devise:install # Tạo file config và các file liên quan rails generate devise user # Tạo model class, routes .. rake db:migrate # Tạo table user rails generate devise:views users # Tạo view
Devise Configuration
Sau khi cài đặt gem devise,bước tiếp theo ta config được xác định dựa trên 2 file chính
- global file, config/initializers/devise.rb. Nếu có gì đó thay đổi trong file này, chúng ta cần phải restart lại server.
- Trong model user nơi đã cài đặt. Có thể tùy chỉnh chức năng thêm
Router
Ở router ta khai báo:
devise_for :users
Sẽ tự động sinh ra các router cần thiết như login, logout, reset password, edit password
# Session routes for Authenticatable (default) new_user_session GET /users/sign_in {controller:"devise/sessions", action:"new"} user_session POST /users/sign_in {controller:"devise/sessions", action:"create"} destroy_user_session DELETE /users/sign_out {controller:"devise/sessions", action:"destroy"} # Password routes for Recoverable, if User model has :recoverable configured new_user_password GET /users/password/new(.:format) {controller:"devise/passwords", action:"new"} edit_user_password GET /users/password/edit(.:format) {controller:"devise/passwords", action:"edit"} user_password PUT /users/password(.:format) {controller:"devise/passwords", action:"update"} POST /users/password(.:format) {controller:"devise/passwords", action:"create"} # Confirmation routes for Confirmable, if User model has :confirmable configured new_user_confirmation GET /users/confirmation/new(.:format) {controller:"devise/confirmations", action:"new"} user_confirmation GET /users/confirmation(.:format) {controller:"devise/confirmations", action:"show"} POST /users/confirmation(.:format) {controller:"devise/confirmations", action:"create"}
Nếu có namspace ta đặt trong namespace:
namespace :publisher do devise_for :account end
Đoạn mã trên sẽ sử dụng publisher/sessions_controller thay vì devise/sessions_controller. Scoping: Bạn có thể đặt devise_for trong một scope:
scope "/my" do devise_for :users end scope ":locale" do devise_for :users end
Bạn có thể yêu cầu config default_url_options trong ApplicationController. tự động thêm locale vào đường link
class ApplicationController < ActionController::Base def self.default_url_options { locale: I18n.locale } end end
Custom router Thay đổi tên đường dẫn nếu bạn k muốn để tên mặc định
devise_scope :user do get "/some/route" => "some_devise_controller" end devise_for :users
Devise Utility Methods
Các helper method hỗ trợ hay dùng và hữu ích nhất là:
- authenticate_user! : authenticate_user! là method ở trong controller. đảm bảo người dùng đã đăng nhập. Hàm này được gọi thông qua before_filter. Ví dụ:
class EndUserBaseController < ApplicationController before_filter :authenticate_user! end
Nếu như user chưa đăng nhập, như mặc dịnh nó sẽ bị redirect về trang login page. Nếu như user đăng nhập thành công, nó sẽ về trang root mà bạn đã cài đặt trong routes, Root routes là route mặc định được định nghĩa trong file config/route.rb
root to: 'tours#index'
Sử dụng authenticate_user! đảm bảo việc user đăng nhập nên ta hoàn toàn có thể sử dụng method current_user để truy cập database hay thao tác trên nó:
class SentMessagesController < EndUserBaseController before_filter :authenticate_user! def index @sent_messages = current_user.sent_messages.all end end
Before_filter :authenticate_user! đảm bảo current_user sẽ không bao giờ bị nil
- current_user: Trả về người dùng đang đăng nhập. Nó sẽ trả về nil khi người dùng chưa đăng nhập
- user_signed_in?: Trả về True hoặc False. True nếu user đã đăng nhập, và ngược lại
- sign_in(@user) , sign_out(@user) : Thực hiện login, logout user
- user_session: Trả về dữ liệu người dùng login.
class User < ActiveRecord::Base devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable, :lockable, :timeoutable end
- database_authenticatable: Đảm bảo mật khẩu được nhập chính xác và mã hóa chúng trước khi lưu vào CSDL
- confirmable: Đảm bảo việc người dùng đăng kí tài khoản sẽ xác nhận tài khoản qua mail mà Devise gửi. Đây là 1 biện pháp để tránh tạo các tài khoản fake. Các option allow_unconfirmed_access_for: Thời gian bạn muốn cho phép người dùng truy cập vào tài khoản của họ trước khi xác nhận. Sau thời gian này, quyền truy cập của người dùng bị từ chối confirm_within: Thời gian có thể confirm. Bạn có thể sử dụng điều này để buộc người sử dụng phải xác nhận trong một khoảng thời gian nhất định. Xác nhận sẽ không tạo mã thông báo mới nếu yêu cầu xác nhận lặp lại. Sử dụng điều này để cho phép người dùng của bạn truy cập một số tính năng của ứng dụng mà không cần xác nhận tài khoản. reconfirmable: Yêu cầu bất kỳ thay đổi email nào đều được xác nhận (chính xác theo cách như xác nhận tài khoản ban đầu) sẽ được áp dụng. Ví dụ:
User.find(1).confirm # returns true unless it's already confirmed User.find(1).confirmed? # true/false User.find(1).send_confirmation_instructions # manually send instructions
- recoverable: Xử lí quên mật khẩu reset_password_keys: Các phím bạn muốn sử dụng khi khôi phục lại mật khẩu cho một tài khoản reset_password_within: Khoảng thời gian trong đó mật khẩu phải được đặt lại hoặc token hết hạn. sign_in_after_reset_password: Có hoặc không đăng nhập người dùng tự động sau khi đặt lại mật khẩu.
- registerable : Cho phép người dùng đăng kí và sau đó thay đổi thông tin đăng nhập
- rememberable: Khi chọn Remember me trên form login. Dựa trên cookie giúp lưu mật khẩu kể từ lần đăng nhập sau
- trackable: Lưu trữ thông tin đăng nhập (địa chỉ IP máy người dùng, thời gian đăng nhập, tổng số lần đăng nhập) Các thông tin được lưu vào các cột: sign_in_count: Tăng sau mỗi lần đăng nhập current_sign_in_at: Đánh dấu thời gian khi người dùng đăng nhập last_sign_in_at: Thời gian đăng nhập trước đó current_sign_in_ip: IP truy cập khi người dùng đăng nhập last_sign_in_ip: IP truy cập lần trước đăng nhập
- validatable : Đảm bảo email, mật khẩu phù hợp với một định dạng cụ thể. Có các option tương ứng là email_regexp: Biểu thức chính quy để xác nhận tính hợp lệ của email, password_length: Xác định độ dài của mật khẩu.
- lockable: Giới hạn số lần đăng nhập sai. Hạn chế truy cập tài khoản trong 1 khoảng thời gian và gửi email bao gồm link để mở khóa tài khoản. Có các options để tùy biến như: maximum_attempts: Số lần nhập sai chấp nhận trước khi khóa người dùng. lock_strategy: khóa người dùng bằng :failed_attempts hoăc :none unlock_strategy: Bỏ khóa tài khoản bằng cách dùng :time, :email, :both hoặc :none. unlock_in: thời gian khóa tài khoản sau khi bị khóa. Chỉ khả dụng khi unlock_strategy là :time hoặc :both. unlock_keys: key sử dụng khi khóa và mở khóa tài khoản. Để tìm hiểu rõ hơn về các option này các bạn có thể tham khảo ở đây
Trong phần này, chúng ta sẽ xem xét một số cách trong đó một số trường hợp sử dụng phổ biến liên quan đến hạn chế truy cập có thể được xử lý bằng các method của Devise
- Trường hợp 1: Đảm bảo tất cả các action trong các controller đều phải đăng nhập trước khi truy cập ngoại trừ trang login và đăng kí
class ApplicationController < ActionController::Base # Ensures all actions invoke this (except those just below) before_filer :authenticate_user! end class AuthenticationController < ApplicationController # Turn off user authentication for all actions in this controller skip_before_filer :authenticate_user! def login '...' end def register '...' end end
- Trường hợp 2: Tất cả các action đều phải đăng nhập trước khi truy cập trừ 1 số action. Ví dụ :index, :show
class CrudController < ApplicationController before_filer :authenticate_user!, except: [ :index, :show ] end class BlogsController < CrudController end class CommentsController < CrudController end
- Trường hợp 3: Giả sử chúng ta muốn phân quyền truy cập cho người dùng,. Admin và user, cả 2 đều được đại diện bởi model riêng biệt. Một model User và Admin.
# All administrator controllers should inherit from this controller class AdminController < ApplicationController before_filer :authenticate_admin! end # All end-user controllers should inherit from this controller class EndUserController < ApplicationController before_filer :authenticate_user! end
- Trường hợp 4: Giả sử chúng ta có người dùng và admin đều chia sẻ chung một model. Tuy nhiên trong trường hợp này admin được xác định bởi 1 flag lưu trong model có giá trị true, false.
class ApplicationController < ActionController::Base before_filer :authenticate_user! end # All administrative controllers should inherit from this controller class AdminController < ApplicationController before_filter :ensure_admin! private def ensure_admin! unless current_user.admin? sign_out current_user redirect_to root_path return false end end end
Bạn có thể tạo ra 1 devise user bằng cách
User.create!(email: "me@home.com", password: "Aa12345")
Một ví dụ rspec
describe BlogController do let :user do User.create!(email: "me@home.com", password: "watching the telly") end before { sign_in user } context "creating a post" do post :create, subject: 'matter', body: 'and soul' expect(assigns(:blog)).to be_instance_of Blog expect(:blog.try :subject).to eq 'matter' expect(:blog.try :user).to eq user end end
Hi vọng bài viết có thể giúp ích cho bạn!
Nguồn tham khảo :
http://www.rubydoc.info/github/plataformatec/devise/master/Devise/Models
https://launchschool.com/blog/how-to-use-devise-in-rails-for-authentication