ด้วยกว่า 20,000 GitHub stars และ integrations เยอะมาก Devise gem เป็นหนึ่งใน gems ยอดนิยมที่สุดใน Ruby landscape ทำไมเราจึง term มันหนึ่งใน "hidden" gems ของ Ruby? ถึงแม้จะยอดนิยม นักพัฒนาส่วนใหญ่แค่ scratch the surface ของ capabilities ของ library
ใน series สองส่วนนี้ เราจะ deep dive เข้า Devise
ในส่วนแรก เราจะเรียนรู้ basics บางอย่าง รวมถึง:
- Devise คืออะไร และทำไมคุณควรใช้มัน รวมถึง situations บางอย่างที่ advisable ไม่ ใช้มัน
- วิธีติดตั้ง Devise และใช้ในโปรเจกต์
- วิธี customize library สำหรับโปรเจกต์
ใน part two เราจะดู advanced usages ของ Devise รวมถึง:
- OmniAuth และการใช้ Devise สำหรับ API-only applications
- Integrating Devise กับ authorization library
มาเริ่มกัน!
Pre-requisites (ข้อกำหนดเบื้องต้น)
ใน tutorial นี้ เราจะใช้ simple Ruby on Rails 7 application ที่มี users และ tasks Users สามารถ register, log in, และ log out พร้อม access ไป create, read, update, และ delete actions สำหรับ tasks ขึ้นอยู่กับ assigned role
เราจะใช้ app นี้เพื่อ progressively build ever more complex features และใน process แสดง Devise's powerful features ใน action
มา kick off ด้วย brief introduction to Devise
แนะนำ Devise สำหรับ Ruby
Devise เป็น authentication library ที่ built on top of Warden Rack-based authentication framework
Warden จัดการ user sessions โดยใช้ secure session strings เพื่อ verify identities ของ logged-in users มันยังจัดการ users ที่ ไม่ logged in เพื่อให้แน่ใจว่าพวกเขาไม่สามารถ access restricted resources
แต่วิธีที่ Warden เป็น purely Rack-based มันไม่ add controller actions, views, helpers หรือ configuration options อื่นๆ ที่จำเป็นสำหรับการสร้าง proper user authentication solution Devise ในอีกมือทำ
คุณสมบัติ notable อีกอย่างของ Devise คือ modularity Library มาพร้อม around 10 modules ที่ให้คุณระบุได้อย่างแม่นยำว่าคุณอยากให้ authentication จัดการในแอปอย่างไร คุณไม่ต้องใช้ทั้ง 10 modules แทนที่จะ activate และใช้แค่สิ่งที่คุณต้องสำหรับแอป เราจะไป over modules นี้ภายหลัง ซึ่งรวมถึง Registerable module, Omniauthable, Trackable และอื่นๆ
ด้วยสิ่งนั้นในใจ มาเริ่มสร้าง Tasks app และติดตั้ง Devise
Getting Started: การติดตั้ง Devise
Generate new Rails 7 app โดยใช้ bundle exec rails new tasks_app หรือแค่ pull example app's code จาก repo
ให้แน่ใจว่าคุณอยู่ใน root directory ของแอป แล้วรัน bundle add devise นี่จะเพิ่ม Devise gem ไป Gemfile ของแอป หลังจากนี้ รัน bundle exec rails g devise:install เพื่อติดตั้ง Devise และ generate initializer file (เราจะดูไฟล์นี้ใกล้ชิดเมื่อเราไป through Devise's modules)
เมื่อคุณรันคำสั่งนี้ generator จะให้ setup suggestions สำหรับ Devise ให้ทำงาน as expected เพียง follow instructions ที่ให้มา และคุณควร good to go
ต่อไป มา generate user model ที่ Devise จะทำงานด้วย
Generating a Devise User Model
Devise ต้องการ user model สิ่งที่คุณเรียก model นี้ totally dependent on preferences และ use case ของแอป แต่ปกติจะเป็น User หรือ Admin
ไปข้างหน้าและ generate Devise user model ด้วย bundle exec rails g devise User นี่จะ generate User model ภายใต้ app/models/user.rb migration file เพื่อสร้าง user table และ route สำหรับ access user resource:
รัน bundle exec rails db:migrate เพื่อรัน migration และ setup user table ด้วยนั้น เราตอนนี้มี user model ที่ใช้ได้สำหรับ all things authentication
ต่อไป มา setup Task model เพื่อใช้ไปข้างหน้า
Generating a Task Model
จำได้ว่าใช้ Devise simple app ของเราต้อง:
- Enable user เพื่อ register
- Enable user เพื่อ sign in และ out ของแอป
- Enable user เพื่อ create Task ที่จะ belong to them
- Allow (หรือ disallow) user จาก interacting กับ Task ขึ้นอยู่กับ role
ด้วยนั้นในใจ มา generate Task model ที่ users จะ interact:
ตอนนี้เราควรมี Task model associated กับ user ที่สร้างมัน title, body และ status (แสดงว่าทำเสร็จหรือยัง)
หลังจากนั้น รัน bundle exec rails db:migrate เพื่อสร้าง tasks table เพิ่มเติม ให้แน่ใจว่า relate Task ไป User:
เมื่อคุณรัน scaffold generator relation belongs_to :users จะ automatically เพิ่มไปที่ Task model ทั้งหมดเดียวกัน แก้ไข Task model ให้ใช้ enum สำหรับ status ตามด้านล่าง:
ด้วยนั้น เราพร้อมที่จะดำดิ่งลึก Devise มาเริ่มด้วย brief overview ของ Devise's modules
Modules ใน Devise สำหรับ Ruby
เช่นที่กล่าวใน introduction หนึ่งใน key features ของ Devise คือ modularity แต "modularity" หมายถึงอะไรในกรณีนี้? มันเพียงแค่หมายถึง separating authentication process เข้า different parts เพื่อ managed independently
Latest version ของ Devise (at the time of writing) มี 10 modules:
- Database authenticatable - Module นี้จะรับ user-supplied password และ convert เป็น secure hash ซึ่งจะ stored ใน database Module ยัง handle verification เมื่อ user signs in
- Omniauthable - Enables OmniAuth authentication
- Lockable - Module นี้จะ lock account ขึ้นอยู่กับจำนวน failed logins Account ทำให้ accessible อีกครั้งหลังจาก certain time period หรือ via email
- Trackable - Tracks จำนวน logins ที่ account ทำ IP addresses ที่ใช้ และ login timestamps
- Confirmable - ส่ง email พร้อม confirmation instructions เมื่อ account registered และจะ check หาก user's account confirmed เมื่อพวกเขา log in
- Registerable - อนุญาต users register account บนแอป และ handles account editing และ destruction
- Recoverable - จัดการ password resets และ account recovery
- Timeoutable - Expires user sessions หลังจาก specified amount of time elapsed
- Validatable - ให้คุณ define custom validation rules สำหรับ user-supplied emails และ passwords ระหว่าง account creation
- Rememberable - ใช้ cookie เพื่อ remember user ระหว่าง authentication
สำหรับข้อมูลเพิ่มเติม Devise's documentation on modules จะ serve you well ตอนนี้เราจะ switch ไป Devise helpers และ filters
Devise Helpers และ Filters
หนึ่งใน reasons ให้ใช้ authentication library อย่าง Devise คือการ manage access ไป controller resources และ associated views Devise มา loaded พร้อม set ของ convenient helpers ซึ่งรวมถึง:
user_signed_in?- Check ว่ามี currently logged-in user หรือไม่current_user- อนุญาตให้คุณ reference currently logged-in user ตัวอย่างเช่น คุณสามารถใช้ code snippet อย่างcurrent_user.emailเพื่อ fetch email ของ currently logged-in useruser_session- สำหรับ currently logged-in user's sessiondestroy_user_session_path- Destroy logged-in user's session และ redirect ไป specified path หรือ root pathnew_user_session_path- Respond ด้วย user login viewedit_user_registration_path- ให้ currently logged-in user access ไป view เพื่อ edit registration detailsnew_user_registration_path- Respond ด้วย view ที่มี registration form สำหรับ register new users
นั่นคือเรื่องของ helpers สำหรับ manage controller access Devise ให้ nifty before_action filter ที่คุณใช้ได้แบบนี้:
ด้วยนั้น มา shift focus ไปที่ Devise integrates กับ Rails' strong parameters อย่างไร
Devise และ Ruby on Rails' Strong Parameters
Strong parameters เป็น well-known Ruby on Rails feature ที่ป้องกัน mass assignment ของ request parameters ไป objects Strong parameters ต้องการ request parameters ถูก explicitly declared ก่อน assignment ใดๆ ทำได้ usually at controller level
Devise ก็ mirror functionality นี้โดย require parameters ถูก explicitly defined under appropriate Devise-specific controller actions:
sign_up- โดย default อยู่ใน Devise controllerDevise::RegistrationsController#createKeys ที่ allowed by default คือemail,password, และpassword_confirmationsign_in- อยู่ใน controllerDevise::SessionsController#newด้วย default allowed authentication keysemailและpasswordaccount_update- อยู่ในDevise::Registrations#update— authentication keysemail,current_password,password, และpassword_confirmationallowed
Most convenient way เพื่อเพิ่ม authentication keys ของคุณเองคือใช้ before_action filter ใน ApplicationController เหมือนที่นี่ ที่เราเพิ่ม username key ที่ required เมื่อ user signs up:
Interesting use case สำหรับ Devise's strong parameters คือเมื่อคุณต้อง pass array of keys ไป Devise sanitizer ตัวอย่างเช่น สมมติเรามี freelancer marketplace app ที่ user สามารถเป็น "contractor" สำหรับ hire หรือ "employer" ที่สามารถ hire contractors อื่นๆ หรือ contractor และ employer พร้อมกัน
โดยไม่ไป into too many technical details เราสามารถ achieve functionality นี้ได้โดย allow users update account โดยใช้ two select boxes สำหรับ respective roles — "contractor" และ "employer" — เพื่อให้ user สามารถเลือก either หรือ both options
Quick way ของการ achieve นี้คือใช้ array with roles as keys แต Rails' strong parameters โดย default allow only scalar values: String, Symbol, NilClass, Numeric, TrueClass, FalseClass, Date, Time, DateTime, StringIO, IO, ActionDispatch::Http::UploadedFile, และ Rack::Test::UploadedFile ตามที่เห็น arrays, hashes และ objects อื่นๆ โดย default ไม่ permitted
เพื่อใช้ array ที่ permit multiple roles สำหรับ user เราสามารถ modify code ตามด้านล่าง:
Devise documentation on helpers ครอบคลุม topic ในรายละเอียดมากกว่า
ต่อไป เราจะเรียนรู้วิธี customize Devise's views และ controllers
การ Customize Devise Views
เพราะ Devise สร้างเป็น Rails engine เกือบทุก components เป็น pre-packaged Good examples ของสิ่งนี้รวมถึง views สำหรับ log in, sign up, update account details, reset password ฯลฯ ในบางจุดของ journey สร้างแอป คุณอาจต้อง customize built-in views เหล่านี้ให้ suit needs
ขั้นแรกคือ generate views โดยใช้คำสั่ง bundle exec rails g devise:views เมื่อคุณทำนั้น respective views จะ generated และ placed ใน folder app/views/devise:

มาใช้ practical example ของการ customize Devise's views ใน previous section เราเรียนรู้วิธีเพิ่ม extra authentication keys ไป Devise sanitizer มา extend example นั้นเพื่อเพิ่ม username field ไป user registration view
เนื่องจาก username field ไม่ available ให้ default user model เราจะเพิ่มมันโดยใช้ bundle exec rails g migration add_column_username_to_user username migration รันคำสั่ง bundle exec rails db:migrate เพื่อเริ่ม migration และเพิ่มคอลัมน์ไป user table
ต่อไป เปิด new user registration file และ edit ดังนี้:
Generating Devise views แบบนี้ fine หากคุณทำงานกับ all views แตพูดว่า คุณแค่อยาก customize a couple of views คุณจะทำอย่างไร? Well คุณ simply pass generator command a list ของ views ที่คุณอยากโดย indicate view flag bundle exec rails g devise:views -v sessions registrations ในกรณีนี้ นี่ only generates views associated กับ login/logout process และที่ handle sign-ups
Moving on มา dig further into Devise customization โดยเรียนรู้วิธี customize library's controllers และ routes
การ Customize Devise Controllers และ Routes
การ customize Devise's views สามารถไปได้แค่ที่ไหน หากคุณอยาก real customization คุณต้องไปเข้า library's controllers และ routes
สมมติเราอยากส่ง email ไป admin แจ้งทุก new user registration บนแอป (นี่ไม่ใช่ ideal way แตมาไปกับ example นี้เพื่อ illustrate สิ่งที่เราอยาก)
เริ่มต้น เราต้อง modify signup action ของ Devise::RegistrationsController ปกติ Devise's controllers ไม่ directly accessible สำหรับ editing ดังนั้นเหมือนที่เราทำกับ views เราสามารถ generate พวกมันได้แบบนี้:
Last bit ของคำสั่งนั้นคือ scope - ในกรณีนี้ users หากคุณอยากมันเป็นอย่างอื่น คุณสามารถ change accordingly เมื่อเสร็จ generated controllers ควรจะอยู่ใน structure คล้ายกับนี้:

ต่อไป เปิด routes file และ modify Devise's routes ให้ reflect changes ใน controller structure:
ตอนนี้เปิด Users::RegistrationsController ที่ generated ใหม่ และ modify เพื่อส่ง email ไป admin ทุกครั้งที่ new user signs up:
ตามที่เห็น ด้วย access ไป Devise's controllers และ actions คุณสามารถ modify authentication flow ของแอปได้อย่างไรก็ตาม
Coming Up Next: Advanced Use of Devise
ในส่วนแรกของ series สองส่วนนี้ เรา cover basics ของ Devise gem เราเรียนรู้ Devise's different modules และวิธี customize views และ controllers
ใน part two เราจะ dive into advanced usages ของ Devise รวมถึง API authentication และวิธีใช้ OmniAuth กับ Devise
Until then happy coding!
P.S. หากคุณต้องการอ่านโพสต์ Ruby Magic ทันทีที่เผยแพร่ ให้สมัครสมาชิก newsletter Ruby Magic ของเรา และจะไม่พลาดโพสต์เด็ดๆ ไป!