30

我一直在开发我的 Rails 应用程序,同时尽可能保持它们的模块化。我试图在下面实现不同的部分作为服务。

以 Facebook 为例:
a) 一个允许用户拥有墙、帖子等的MainApp。b) 一个存储照片、允许用户查看他的照片等的 PhotoApp是一个独立的应用程序,它将具有MainApp 也可以使用 REST API。

我正在考虑使用 OAuth 作为单点登录解决方案(如本教程http://blog.joshsoftware.com/2010/12/16/multiple-applications-with-devise-omniauth-and-single-sign-on / ) 其中每个应用程序都将通过 OAuth 进行授权,并将根据 cookie 访问当前用户会话。

第一个问题:这是一个可行的解决方案吗?

第二个问题:我希望能够从MainApp服务器(而不是用户的浏览器)调用PhotoApp API在这种情况下身份验证如何工作?

第三个问题:如果说我有一个使用 node.js 的服务,这将如何工作?

4

3 回答 3

20

是的,使用 OAuth 的 SSO 是一种可行的解决方案,但它并不是最简单的解决方案。在构建任何新事物时,OAuth 2.0是必经之路。OAuth 标准涵盖了很多领域。

OAuth 的主要优点是它允许用户在不向 3rd 方透露密码的情况下让 3rd 方应用程序访问他们的帐户。如果您没有认真提供这种互操作性,那么 OAuth 可能是矫枉过正。

鉴于复杂性,我提供了一对不同的解决方案:

对于单点登录

诀窍是在域内的主机之间共享会话 ID 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,否则共享密钥可能会在网络上被嗅探。

于 2012-11-03T17:54:49.517 回答
4

您可以查看 Jeremy Green 在 Octolabs 在 2014 RailsConf 期间提出的面向服务的架构解决方案。

包含所有资源(repos、演示等)的博客文章位于此处:http ://www.octolabs.com/so-auth

解释一切的视频在这里:http ://www.youtube.com/watch?v=L1B_HpCW8bs

这个集中式 SSO 不是一项简单的任务,但 Jeremy 在谈论面向服务的架构方面做得非常出色,并准确地分享了您如何将这个系统组合在一起。

于 2015-01-18T16:06:31.770 回答
3

我最近遇到了一个类似的问题,想在 Rails 和 Erlang 应用程序之间共享会话数据。我的解决方案是编写一个Rack::Session::Abstract::ID类,将 Redis 中的会话存储为哈希值。它不调用Marshal.dump类型String。这允许非 ruby​​ 应用程序使用某些会话值(如果它们具有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

您可以从 rails 使用它:

 MyApp::Application.config.session_store MaybeMarshalRedisSession

从机架:

 use MaybeMarshalRedisSession

并从其他地方:

redis.hgetall("rack.session:#{session_id}")

如果您想从 MainApp 或 Node.js 调用 PhotoApp,您可以发出包含用户会话 cookie 的 HTTP 请求。

于 2012-11-03T05:45:16.407 回答