验证 ActionCable 连接

Authenticating ActionCable connections

我找到了一个很棒的 ActionCable gem,这是一个很好的 SPA 解决方案。

我只想发送 htmlcssjs 资产,所有其他连接将通过 ActionCable 实现。交换字符串或整数并不难,但是如何通过 ActionCable 登录?

来自Readme

# app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    protected
      def find_verified_user
        if current_user = User.find(cookies.signed[:user_id])
          current_user
        else
          reject_unauthorized_connection
        end
      end
  end
end

看来您可以在此处插入您自己的 find_verified_user 逻辑。 reject_unauthorized_connection 方法存在于 lib/action_cable/connection/authorization.rb 以供参考。

来自Heroku

[authentication] can be done in a variety of ways, as WebSockets will pass through standard HTTP headers commonly used for authentication. This means you could use the same authentication mechanism you’re using for your web views on WebSocket connections as well.

Since you cannot customize WebSocket headers from JavaScript, you’re limited to the “implicit” auth (i.e. Basic or cookies) that’s sent from the browser. Further, it’s common to have the server that handles WebSockets be completely separate from the one handling “normal” HTTP requests. This can make shared authorization headers difficult or impossible.

考虑到这一点,如果不使用普通的 Web 登录流程来设置您的身份验证 cookie,在身份验证步骤后交付您的 SPA,这可能会是一个真正的痛苦,但希望这可以给您一些指示。

解决方案是使用 HTTP 授权令牌。它简单、广泛且显而易见。 This article对我帮助很大

仅供参考,如果您的应用程序中已经安装了 devise,那么您可以使用 warden 设置的环境变量来查找 authenticated user。对于每个经过身份验证的用户,监狱长将用户对象存储在环境变量中。每个请求都由 warden 中间件进行身份验证。

注意:这个env不同于ENV

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user_from_env
    end

    private
    def find_verified_user_from_env
      # extracting `user` from environment var
      current_user = env['warden'].user
      if current_user
        current_user
      else
        reject_unauthorized_connection
      end
    end
  end
end

如果你还没有用过devise那么,这里有另一种解决方法。前提是,您必须在 sessions_controller 或类似的东西中设置一个名为 user_id 的签名 cookie。 例如

cookies.signed[:user_id] = current_user.id

和连接:

# app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user_from_cookies
    end

    private
    def find_verified_user_from_cookies
      current_user = User.find_by_id(cookies.signed[:user_id])
      if current_user
        current_user
      else
        reject_unauthorized_connection
      end
    end
  end
end