Аутентификация Rails через приложения/серверы
Я разрабатывал свои приложения для рельсов, сохраняя их как можно более модульными. Я пытаюсь реализовать различные части под услугами.
Скажите пример Facebook:
a) A MainApp, который позволяет пользователю иметь стену, сообщения и т.д.
b) A PhotoApp, в котором хранятся фотографии, позволяет пользователю видеть его фотографии и т.д. Это автономное приложение, которое будет иметь REST API, который также может использоваться MainApp.
Я думал об использовании OAuth в качестве решения Single Sign On (как в этом уроке http://blog.joshsoftware.com/2010/12/16/multiple-applications-with-devise-omniauth-and-single-sign-on/), где каждое приложение будет авторизовано через OAuth и получит доступ к текущему сеансу пользователя на основе файла cookie.
Первый вопрос: Является ли это жизнеспособным решением?
Второй вопрос: Я хочу иметь возможность вызывать API PhotoApp на сервере MainApp (а не в браузере пользователя). Как аутентификация будет работать в этой ситуации?
Третий вопрос: Как это работает, если скажу, что у меня была служба, которая использовала node.js?
Ответы
Ответ 1
Да, SSO, использующий OAuth, является жизнеспособным решением, но это не самый простой. При создании чего-либо нового OAuth 2.0 - это путь. Стандарты OAuth охватывают множество факторов.
Основное преимущество OAuth заключается в том, что он позволяет пользователям предоставлять сторонним приложениям доступ к своей учетной записи, не раскрывая их пароль третьей стороне. Если вы серьезно не обеспечиваете такую функциональную совместимость, то OAuth, вероятно, будет излишним.
Учитывая сложность, я предлагаю разные пары решений:
Для одиночного входа
Хитрость заключается в совместном использовании cookie идентификатора сеанса между хостами в вашем домене и использовании общего хранилища сеансов (например, ActiveRecordStore или хранилища на основе кеша).
Каждое приложение Rails имеет "секрет", который используется для подписывания файлов cookie. В новых приложениях Rails это находится в /config/initializers/secret_token.rb
. Установите один и тот же секретный токен в каждом приложении.
Затем настройте сеанс, чтобы разрешить доступ со всех поддоменов:
AppName::Application.config.session_store :active_record_store, :key => '_app_name_session', :domain => :all
Для вызовов внутреннего API
Используйте общедоступный секрет для аутентификации через HTTPS-соединения. Передайте секрет в значении заголовка "Авторизация".
Вы можете легко использовать общий секрет с другими архитектурами (например, node.js). Просто убедитесь, что вы всегда используете HTTPS, иначе общий секрет можно было бы понюхать в сети.
Ответ 2
Недавно у меня возникла аналогичная проблема, связанная с необходимостью совместного использования данных сеанса между Rails и приложением Erlang. Моим решением было написать класс Rack::Session::Abstract::ID
, который хранит сеансы в Redis как hash vaules. Он не вызывает Marshal.dump
для типов String
. Это позволяет нерубным приложениям использовать некоторые из значений сеанса, если они имеют session_id
.
require 'rack/session/abstract/id'
class MaybeMarshalRedisSession < Rack::Session::Abstract::ID
def initialize(app, options = {})
@redis = options.delete(:redis) || Redis.current
@expiry = options[:expire_after] ||= (60 * 60 * 24)
@prefix = options[:key] || 'rack.session'
@session_key = "#{@prefix}:%s"
super
end
def get_session(env, sid)
sid ||= generate_sid
session = @redis.hgetall(@session_key % sid)
session.each_pair do |key, value|
session[key] = begin
Marshal.load(value)
rescue TypeError
value
end
end
[sid, session]
end
def set_session(env, sid, session, options={})
@redis.multi do
session.each_pair do |key, value|
# keep string values bare so other languages can read them
value = value.is_a?(String) ? value : Marshal.dump(value)
@redis.hset(@session_key % sid, key, value)
end
@redis.expire(@session_key % sid, @expiry)
end
sid
end
def destroy_session(env, sid, option={})
@redis.del(@session_key % sid)
generate_sid unless options[:drop]
end
end
Вы можете использовать это из рельсов с помощью:
MyApp::Application.config.session_store MaybeMarshalRedisSession
Из стойки с:
use MaybeMarshalRedisSession
И из других источников:
redis.hgetall("rack.session:#{session_id}")
Если вы хотите вызвать PhotoApp из вашего MainApp или Node.js, вы можете сделать HTTP-запрос, содержащий ваш cookie сеанса пользователя.
Ответ 3
Вы можете взглянуть на решение сервис-ориентированной архитектуры, предложенное Джереми Грин в Octolabs во время RailsConf 2014 года.
Сообщение в блоге со всеми ресурсами (repos, demos и т.д.) находится здесь: http://www.octolabs.com/so-auth
И видео, которое объясняет все, находится здесь: http://www.youtube.com/watch?v=L1B_HpCW8bs
Этот централизованный SSO - не простая задача, но Джереми отлично поработал над сервис-ориентированной архитектурой и делится тем, как можно объединить эту систему.