6

最近我在使用 Mechanize 来做这种事情,但我想使用 Typhoeus,我已经在其他地方使用了它。我想模仿 Mechanize 的行为,问题是我想登录到一个站点并以登录用户的身份执行请求。这是脚本的通用版本:

require 'rubygems'
require 'typhoeus'

GET_URL = 'http://localhost:3000'
POST_URL = "http://localhost:3000/admins/sign_in"
URL = "http://localhost:3000/dashboard"
USERNAME_FIELD = 'admin[email]'
PASSWORD_FIELD = 'admin[password]'
USERNAME = "admin@example.com"
PASSWORD = "my_secret_password"

def merge_cookies_into_cookie_jar(response)                            
  if response.headers_hash['set-cookie'].instance_of? Array
    response.headers_hash['set-cookie'].each do |cookie|
      @cookie_jar << cookie.split('; ')[0]
    end
  elsif response.headers_hash['set-cookie'].instance_of? String
    @cookie_jar << response.headers_hash['set-cookie'].split('; ')[0]
  end        
end

# initialize cookie jar
@cookie_jar = []

# for server to establish me a session
response = Typhoeus::Request.get( GET_URL, :follow_location => true )
merge_cookies_into_cookie_jar(response)                                                   

# like submiting a log in form
response = Typhoeus::Request.post( POST_URL,
                                   :params => { USERNAME_FIELD => USERNAME, PASSWORD_FIELD => PASSWORD },
                                   :headers => { 'Cookie' => @cookie_jar.join('; ') }
                                 )
merge_cookies_into_cookie_jar(response)                                                   

# the page I'd like to get in a first place, 
# but not working, redirects me back to login form with 401 Unauthorized :-(                 
response = Typhoeus::Request.get( URL, 
                                  :follow_location => true,
                                  :headers => { 'Cookie' => @cookie_jar.join('; ') }
                                 )

cookie 被发送到服务器,但由于某种原因我没有登录。我在两个不同的站点上测试了它(其中一个是我的 Rails 应用程序的管理)。知道我做错了什么,或者可能是针对此问题的更好或更广泛适用的解决方案吗?

4

3 回答 3

8

这是我能够成功运行的代码。

首先,您的 cookie jar 是一个数组,在我的代码中它需要是一个带替换(或哈希)的数组。当我在我的真实应用程序上运行代码时,GET_URL 响应返回一个会话 cookie,但 POST_URL 响应返回一个不同的会话 cookie。

# initialize cookie jar as a Hash
@cookie_jar = {}

调整解析,以便获得每个 cookie 的名称和值:

def merge_cookies_into_cookie_jar(response)
  x =  response.headers_hash['set-cookie']
  case x
  ...
  when String
    x.split('; ').each{|cookie|
      key,value=cookie.split('=', 2)
      @cookie_jar[key]=value
    }
  end
end

cookie jar 需要转换为字符串:

def cookie_jar_to_s
  @cookie_jar.to_a.map{|key, val| "#{key}=#{val}"}.join("; ")
end

最后,更改标题以使用新的 cookie_jar_to_s:

:headers => { 'Cookie' => cookie_jar_to_s }

一个好处是让 cookie jar 成为它自己的类,可能是这样的:

class CookieJar < Hash

  def to_s
    self.to_a.map{|key, val| "#{key}=#{val}"}.join("; ")
  end

  def parse(*cookie_strings)
    cookie_strings.each{|s|
      s.split('; ').each{|cookie|
        key,value=cookie.split('=', 2)
        self.[key]=value
      }
    }
  end

end
于 2012-03-26T06:25:49.703 回答
4

我已经修复了@joelparkerhenderson 的 CookieJar(因为它在这里不起作用)。结果如下:

class CookieJar < Hash
  def to_s
    self.map { |key, value| "#{key}=#{value}"}.join("; ")
  end

  def parse(cookie_strings)
    cookie_strings.each { |s|
      key, value = s.split('; ').first.split('=', 2)
      self[key] = value
    }
    self
  end
end

# Use like this:
response = Typhoeus::Request.get("http://www.example.com")
cookies = CookieJar.new.parse(response.headers_hash["Set-Cookie"])
Typhoeus::Request.get("http://www.example.com", headers: {Cookie: cookies.to_s})
于 2012-10-04T07:18:42.450 回答
1

您的网站是否使用 Rails 防伪保护?

如果是这样,当您获得表单页面时,Rails 将发送一个隐藏字段,即 CSRF 令牌。

它在 HTML 中看起来像这样:

<input type="hidden" name="csrf" value="abcdef">

发布表单时需要使用此隐藏值:

:params => {"csrf" => "abcdef", USERNAME_FIELD => USERNAME, ...

隐藏字段告诉 Rails 您是请求表单页面的人,因此您(并且只有您)被允许发布。

这是我关于 CSRF 的笔记,其中包含指向更多信息的链接:

http://sixarm.com/about/rails-session-csrf-token-jquery-ajaxprefilter.html

相关 StackOverflow CSRF 信息:

http://stackoverflow.com/questions/941594/understand-rails-authenticity-token
于 2012-03-25T21:17:15.873 回答