CGI::Session
class CGI::Session
Parent:Object
Overview
该文件提供CGI :: Session类,它为CGI脚本提供会话支持。会话是一系列HTTP请求和响应链接在一起并与单个客户端相关联。与会话相关的信息在请求之间存储在服务器上。会话ID在客户端和服务器之间传递,每个请求和响应对用户都是透明的。这将状态信息添加到否则无状态的HTTP请求/响应协议。
生命周期
CGI :: Session实例是从CGI对象创建的。默认情况下,如果当前不存在该CGI :: Session实例,将启动一个新会话,如果存在,则继续此客户端的当前会话。该new_session
选项可用于始终或永远不会创建新会话。有关更多详细信息,请参阅new()。
delete()从会话存储中删除一个会话。但它不会从客户端中删除会话标识。如果客户端使用相同的ID发出另一个请求,则效果将是使用旧会话的ID开始新会话。
设置和检索会话数据。
Session类将数据与会话关联为键值对。可以通过使用'[]'索引Session实例来设置和检索这些数据,这与散列(尽管不支持其他散列方法)非常相似。
当一个请求的会话处理已经完成时,会话应该使用close()方法关闭。这会将会话的状态存储到持久存储中。如果您想要将会话的状态存储到持久性存储,而不完成此请求的会话处理,请调用update()方法。
存储会话状态
调用者可以使用database_manager
:: new选项指定会话数据使用的存储形式。以下存储类作为标准库的一部分提供:
CGI::Session::FileStore
将数据作为纯文本存储在平面文件中。仅适用于String数据。这是默认的存储类型。
CGI::Session::MemoryStore
将数据存储在内存散列中。只要当前的Ruby解释器实例有效,数据就会一直存在。
CGI::Session::PStore
以Marshalled格式存储数据。由cgi / session / pstore.rb提供。支持任何类型的数据,并提供文件锁定和事务支持。
自定义存储类型也可以通过使用以下方法定义类来创建:
new(session, options)
restore # returns hash of session data.
update
close
delete
更改中会话的存储类型不起作用。特别要注意的是,默认情况下,FileStore和PStore会话数据文件具有相同的名称。如果您的应用程序从一个切换到另一个,而没有确定文件名会不同并且客户端仍然有旧的会话存在于Cookie中,那么事情就会突然崩溃!
维护会话ID。
大多数会话状态在服务器上维护。但是,会话ID必须在客户端和服务器之间反向传递以维护对此会话状态的引用。
最简单的方法是通过cookie。如果客户端启用了Cookie,则CGI :: Session类将为透过Cookie的会话ID通信提供透明支持。
如果客户端禁用了Cookie,则会话ID必须包含为客户端发送到服务器的所有请求的参数。与CGI类一起使用的CGI :: Session类将透明地将会话标识添加为使用CGI#form()HTML生成方法生成的所有表单的隐藏输入字段。没有为其他机制提供内置支持,例如URL重写。调用者负责从#session_id属性中提取会话ID并手动将其编码到URL中,并将其添加为由其他机制创建的HTML表单的隐藏输入。此外,会话过期不会自动处理。
使用示例
Setting the user's name
require 'cgi'
require 'cgi/session'
require 'cgi/session/pstore' # provides CGI::Session::PStore
cgi = CGI.new("html4")
session = CGI::Session.new(cgi,
'database_manager' => CGI::Session::PStore, # use PStore
'session_key' => '_rb_sess_id', # custom session key
'session_expires' => Time.now + 30 * 60, # 30 minute timeout
'prefix' => 'pstore_sid_') # PStore option
if cgi.has_key?('user_name') and cgi['user_name'] != ''
# coerce to String: cgi[] returns the
# string-like CGI::QueryExtension::Value
session['user_name'] = cgi['user_name'].to_s
elsif !session['user_name']
session['user_name'] = "guest"
end
session.close
安全地创建新的会话
require 'cgi'
require 'cgi/session'
cgi = CGI.new("html4")
# We make sure to delete an old session if one exists,
# not just to free resources, but to prevent the session
# from being maliciously hijacked later on.
begin
session = CGI::Session.new(cgi, 'new_session' => false)
session.delete
rescue ArgumentError # if no old session
end
session = CGI::Session.new(cgi, 'new_session' => true)
session.close
属性
new_sessionR
此会话的ID。
session_idR
此会话的ID。
公共类方法
new(request, option={}) Show source
Create a new CGI::Session object for request
.
request
是CGI
该类的一个实例(请参阅cgi.rb)。option
是用于初始化此CGI
:: Session实例的选项的哈希。以下选项被认可:
session_key
用于会话ID的参数名称。默认为'_session_id'。
#session_id
要使用的会话标识。如果未提供,则从session_key
请求的参数中检索它,或为新会话自动生成。
#new_session
如果属实,则强制创建新会话。如果没有设置,只有当前不存在的情况下才会创建一个新的会话。如果为false,则永远不会创建新的会话,如果当前没有任何会话并且session_id
未设置该选项,则会引发ArgumentError。
database_manager
为会话状态持久性提供存储设施的类的名称。为FileStore
(默认)MemoryStore
,和PStore
(从cgi / session / pstore.rb)提供了内置支持。有关更多详细信息,请参阅这些类的文档。
以下选项也被识别,但仅适用于会话标识存储在cookie中的情况。
session_expires
当前会话到期的时间,作为Time
对象。如果未设置,会话将在用户浏览器关闭时终止。
session_domain
此会话有效的主机名域。如果未设置,则默认为服务器的主机名。
session_secure
如果true
此会话只能通过HTTPS进行工作。
session_path
此会话适用的路径。默认为CGI脚本的目录。
option
也传递给会话存储类初始化程序; 请参阅每个会话存储类的文档以了解它们支持的选项。
检索到的或创建的会话会自动添加request
为cookie,也会自动添加到其output_hidden
表中,该表用于将隐藏的输入元素添加到表单中。
警告这些output_hidden字段被HTML 4代中的<fieldset>标记包围,这在许多浏览器中都是不可见的; 您可能希望禁用使用类似于以下代码的字段集(请参阅blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/37805)
cgi = CGI.new("html4")
class << cgi
undef_method :fieldset
end
# File lib/cgi/session.rb, line 247
def initialize(request, option={})
@new_session = false
session_key = option['session_key'] || '_session_id'
session_id = option['session_id']
unless session_id
if option['new_session']
session_id = create_new_id
@new_session = true
end
end
unless session_id
if request.key?(session_key)
session_id = request[session_key]
session_id = session_id.read if session_id.respond_to?(:read)
end
unless session_id
session_id, = request.cookies[session_key]
end
unless session_id
unless option.fetch('new_session', true)
raise ArgumentError, "session_key `%s' should be supplied"%session_key
end
session_id = create_new_id
@new_session = true
end
end
@session_id = session_id
dbman = option['database_manager'] || FileStore
begin
@dbman = dbman::new(self, option)
rescue NoSession
unless option.fetch('new_session', true)
raise ArgumentError, "invalid session_id `%s'"%session_id
end
session_id = @session_id = create_new_id unless session_id
@new_session=true
retry
end
request.instance_eval do
@output_hidden = {session_key => session_id} unless option['no_hidden']
@output_cookies = [
Cookie::new("name" => session_key,
"value" => session_id,
"expires" => option['session_expires'],
"domain" => option['session_domain'],
"secure" => option['session_secure'],
"path" =>
if option['session_path']
option['session_path']
elsif ENV["SCRIPT_NAME"]
File::dirname(ENV["SCRIPT_NAME"])
else
""
end)
] unless option['no_cookies']
end
@dbprot = [@dbman]
ObjectSpace::define_finalizer(self, Session::callback(@dbprot))
end
公共实例方法
显示来源
检索密钥的会话数据key
。
# File lib/cgi/session.rb, line 308
def [](key)
@data ||= @dbman.restore
@data[key]
end
[]=(key, val) 显示源
设置密钥的会话数据key
。
# File lib/cgi/session.rb, line 314
def []=(key, val)
@write_lock ||= true
@data ||= @dbman.restore
@data[key] = val
end
close() 显示源
将会话数据存储在服务器上并关闭会话存储。对于某些会话存储类型,这是无操作。
# File lib/cgi/session.rb, line 328
def close
@dbman.close
@dbprot.clear
end
delete() 显示源
从存储中删除会话。也关闭存储。
请注意,会话过期后会话的数据不会
自动删除。
# File lib/cgi/session.rb, line 337
def delete
@dbman.delete
@dbprot.clear
end
update() 显示源
将会话数据存储在服务器上。对于某些会话存储类型,这是无操作。
# File lib/cgi/session.rb, line 322
def update
@dbman.update
end
私有实例方法
create_new_id() 显示源
创建一个新的会话ID。
如果可能的话,会话ID是SecureRandom的安全随机数,否则是基于时间,随机数和常量字符串的SHA512哈希。此例程在内部用于自动生成的会话ID。
# File lib/cgi/session.rb, line 170
def create_new_id
require 'securerandom'
begin
# by OpenSSL, or system provided entropy pool
session_id = SecureRandom.hex(16)
rescue NotImplementedError
# never happens on modern systems
require 'digest'
d = Digest('SHA512').new
now = Time::now
d.update(now.to_s)
d.update(String(now.usec))
d.update(String(rand(0)))
d.update(String($$))
d.update('foobar')
session_id = d.hexdigest[0, 32]
end
session_id
end