Warden - це Rack middleware, що забезпечує механізм аутентифікації для веб-додатків на Ruby.
Warden вимагає деяких налаштувань для Sinatra. Цього можна уникнути, використовуючи плагін Sinatra::Warden. Але ми ж не шукаємо простих шляхів.
Налаштування DataMapper
Для початку створимо модель і таблицю бази даних для зберігання імені користувача і паролю:class User
include DataMapper::Resource
property :id, Serial
property :username, String, :unique => true
property :password, String
end
Налаштування Rack
Повинен бути оголошений додаток для невдач, а також, які стратегії аутентифікації використовуватимуться по замовчуванню.use Rack::Session::Cookie, :secret => "bla-bla-bla"
use Warden::Manager do |manager|
manager.default_strategies :password
manager.failure_app = FailureApp.new
end
class FailureApp
def call(env)
uri = env['REQUEST_URI']
puts "failure #{env['REQUEST_METHOD']} #{uri}"
end
end
Налаштування сесії
Одним з результатів використання будь-якого об'єкта, наприклад об'єкта User, є те, що ви повинні сказати Warden, як серіалізувати його у та із сесії. Це потрібно налаштувати:Warden::Manager.serialize_into_session do |user|
user.id
end
Warden::Manager.serialize_from_session do |id|
User.get(id)
end
Після успішної аутентифікації в сесії буде зберігатися ідентифікатор користувача.Стратегія аутентифікації
Стратегія у Warden містить логіку для перевірки аутентифікації запиту.Warden::Strategies.add(:password) do
def valid?
params['username'] || params['password']
end
def authenticate!
u = User.authenticate(params['username'], params['password'])
u.nil? ? fail!('Could not login in') : success!(u)
end
end
Вище ми оголосили стратегію під назвою :password, яка містить два стандартні методи #valid? і #authenticate!.Необов'язковий метод #valid? діє в якості охорони для стратегії. Якщо ви не визначите його, стратегія завжди буде працювати. В іншому випадку стратегія виконуватиметься тільки якщо #valid? поверне значення true.
Стратегія, яку ми оголосили вище передбачає, що якщо переданий хоча б один з параметрів 'username' чи 'password', то користувач намагається увійти. Якщо є тільки один з ним, то далі виклик User#authenticate закінчиться невдачею.
У методі #authenticate! відбувається аутентифікація. Він містить логіку для перевірки аутентифікації запитів.
Тут же і містяться дві стандартні дії: fail! і success!.
success! встановлює успішну аутентифікацію. Екземпляром класу User серіалізується у сесію.
fail! встановлює, що стратегію пройшла безуспішно.
Для перевірки аутентифікації будемо використовувати відкритий метод User#authenticate, який повертатиме об'єкт User, якщо передане коректне ім'я користувача і пароль:
class User
include DataMapper::Resource
# ...
def self.authenticate(username, password)
user = first(:username => username)
if user
if user.password != password
user = nil
end
end
user
end
end
Звичайно, цей метод написаний з метою демонстрації. Насправді, логіка аутентифікації може бути настільки складною, наскільки це необхідно.Після успішної аутентифікації, екземпляр класу User серіалізується (Warden::Manager.serialize_into_session) у сесію. Далі його можна отримати через змінну оточення env['warden'].user, шляхом десеріалізації (Warden::Manager.serialize_from_session) по ідентифікатору.
Маршрути Sinatra
Ось ми і дісталися до завершальної стадії нашої статті. Настав час визначити маршрути для для нашого веб-додатку.get '/' do
# ...
end
get '/login/?' do
# ...
end
post '/login/?' do
# ...
end
get '/logout/?' do
# ...
end
На головній сторінці виводитиметься привітання для користувача, якщо він автентифікований на сайті. Інакше переправлятиметься на сторінку '/login' з формою для аутентифікації.
get '/' do
redirect '/login' unless env['warden'].user
slim :index
end
get '/login/?' do
slim :login
end
Головна сторінка:
p Welcome, #{env['warden'].user.username}
a href='/logout' Log out
Форма для аутентифікації містить для текстових поля 'username' і 'password'.
form action='/login' method='post'
ul
li#username
label Username:
br
input name='username' type='text'
li#password
label Password:
br
input name='password' type='text'
input type='submit' value='Log in'
Після того як користувач введе ім'я і пароль відбувається перевірка аутентифікації. І якщо вона пройшла успішно, користувач переправляється на головну сторінку з привітанням. Ця ж сторінка містить посиланням для виходу.
post '/login/?' do
if env['warden'].authenticate
redirect '/'
else
redirect '/login'
end
end
get '/logout/?' do
env['warden'].logout
redirect '/'
end
На всяк випадок, викладу тут повний код додатку.
Немає коментарів:
Дописати коментар