C4 always sleepy

28Sep/094

SSO на Rails с использованием Authlogic

Вопрос SSO для многих актуален, различных решений великое множество (OpenId, Oauth, CAS итд.), но иногда хочется сделать все по своему. Давайте попробуем написать свой SSO для наших сайтов?

Давайте для начала определим, что нам необходимо сделать:
1. Разработать центральный сервис аутентификации
2. Разработать сайт, который будет использовать SSO
2. Обеспечить доступность идентификатора сессии в рамках всех наших сайтов
3. Обеспечить доступность сессионных данных
4. Обеспечить доступность пользовательских данных

Давайте приступим к процессу разработки.

1. Первым делом нам необходимо реализовать сам сервис аутентификации. Про это уже много написано, например ознакомиться можно в соответствующем railscast или asciicast.

2. Думаю тут я могу обойтись без лишних комментариев, единственное что вам необходимо селать - это включить authlogic, создать модели User и UserSession:

class User < ActiveRecord::Base
  establish_connection "auth_#{RAILS_ENV}"
  acts_as_authentic
end

(на строку establish_connection пока не обращаем внимания - мы к ней еще подойдем :) )

class UserSession < Authlogic::Session::Base
end

(обратите внимание на сложность данного класса :) )

а также дополним ApplicationController базовым набором методов

  private

  def current_user_session
    return @current_user_session if defined?(@current_user_session)
    @current_user_session = UserSession.find
  end

  def current_user
    return @current_user if defined?(@current_user)
    @current_user = current_user_session && current_user_session.record
  end

  def require_user
    unless current_user
      store_location
      flash[:notice] = I18n.t(:"notices.require_logged_in")
      redirect_to login_url
      return false
    end
  end

  def require_no_user
    if current_user
      store_location
      flash[:notice] = I18n.t(:"notices.require_logged_out")
      redirect_to dashboard_url
      return false
    end
  end

  # Store the URI of the current request in the session.
  #
  # We can return to this location by calling #redirect_back_or_default.
  def store_location
    session[:return_to] = request.request_uri
  end

  # Redirect to the URI stored by the most recent store_location call or
  # to the passed default.  Set an appropriately modified
  #   after_filter :store_location, :o nly => [:index, :new, :show, :edit]
  # for any controller you want to be bounce-backable.
  def redirect_back_or_default(default)
    redirect_to(session[:return_to] || default)
    session[:return_to] = nil
  end

2. Идентификатор сессии будем хранить в cookies. Для того, чтобы он был доступен для всех сайтов на домене *.example.com (auth.example.com, site1.exampe.com итд) нам по идее достаточно просто написать

ActionController::Base.session = {
  :domain => ".example.org"
}

но знающие люди рекомендуют такой вот замечательный gist. Сохраняем его как файл
session_domain.rb в conf/initializers/, а также вносим небольшие изменения в session_store.rb:

ActionController::Base.session = {
  :key         => '_example_session',
  :secret      => 'oursupersecretkey'
}

Самое время перейти к следующему пункту :)

3. Не закрывая session_store.rb продолжаем вносить в него изменения:

ActionController::Base.session_store = :active_record_store

Для сайтов, которые будут использовать интерфейс SSO (а также если вы захотите вынести хранилище сессий в отдельную БД) порписываем в этом же файле

# Use sessions from preconfigured session store
ActiveRecord::SessionStore::Session.establish_connection("sessions_#{RAILS_ENV}")

а в файле database.yml:

# Sessions stores

sessions_development:
  adapter: sqlite3
  database: ../auth/db/development.sqlite3
  pool: 5
  timeout: 5000

sessions_test:
  adapter: sqlite3
  database: ../auth/db/test.sqlite3
  pool: 5
  timeout: 5000

sessions_production:
  adapter: sqlite3
  database: ../auth/db/production.sqlite3
  pool: 5
  timeout: 5000

4. Помните пару абзацев назад я просил не обращать внимания на строчку кода? Самое время к ней вернуться. Класс User будет использовать соединение с БД нашего сервиса аутентификации для получения информации о текущем пользователе. Обновим данные в database.yml:

auth_development:
  adapter: sqlite3
  database: ../auth/db/development.sqlite3
  pool: 5
  timeout: 5000

auth_test:
  adapter: sqlite3
  database: ../auth/db/test.sqlite3
  pool: 5
  timeout: 5000

auth_production:
  adapter: sqlite3
  database: ../auth/db/production.sqlite3
  pool: 5
  timeout: 5000

Вот в принципе и все :) Не сложно, правда? Теперь на site1.example.com мы спокойно сможем работать с current_user и пользовательской сессией.

PS: Возможно решение не идеальное, но с удовольствием рассмотрю все ваши рекомендации по его оптимизации :)

2Mar/090

Системы аутентификации

Недавно ко мне обратился один из коллег проконсультироваться по поводу аутентификации пользователей и я бы хотел узнать у читателей блога - а что вы используете? Какие системы, какие библиотеки, ваши про и контра?

От себя скажу, что использовал пока что только самописные системы, но используя Spring Security для java-приложений и before-filter для рельсов :)

Tagged as: No Comments