Ruby CGI Session
在 Ruby 的 CGI(Common Gateway Interface) 编程中,Session(会话) 是一种用于在多个 HTTP 请求之间维护用户状态的机制。HTTP 是无状态协议,而 Session 通过在服务器端存储数据并通过标识(如 Cookie)关联用户请求,解决了状态保持的问题。Ruby 的 cgi
库提供了 CGI::Session
类来管理会话。以下是对 Ruby CGI Session 的中文讲解,涵盖核心概念、创建与使用 Session、示例代码及注意事项,力求简洁清晰,并避免重复之前关于 CGI 的内容(如基本 CGI 方法)。
1. Session 简介
Session 允许在服务器端存储用户数据(如用户 ID、购物车内容),通过唯一的 Session ID(通常存储在 Cookie 中)在客户端和服务器之间关联。Ruby 的 CGI::Session
类提供了简单的方式管理 Session,支持文件存储、内存存储等。
用途:
- 跟踪用户登录状态。
- 保存临时数据(如表单输入)。
- 实现跨页面状态保持。
2. CGI::Session 类
CGI::Session
用于创建和管理会话,数据默认存储在服务器端的临时文件中,Session ID 通过 Cookie 传递给客户端。
创建 Session
require 'cgi'
require 'cgi/session'
cgi = CGI.new
session = CGI::Session.new(cgi, options)
常用选项:
database_manager
:存储方式(默认CGI::Session::FileStore
,文件存储)。session_key
:Cookie 名称(默认_session_id
)。session_id
:指定 Session ID(默认自动生成)。new_session
:是否强制创建新会话(true
/false
)。tmpdir
:临时文件存储目录(默认系统临时目录)。prefix
:Session 文件名前缀。
示例:
session = CGI::Session.new(cgi, 'database_manager' => CGI::Session::FileStore, 'tmpdir' => '/tmp')
3. 操作 Session
Session 像哈希一样操作,键值对存储用户数据。
存储数据
session['user_id'] = '12345'
session['username'] = 'Alice'
读取数据
user_id = session['user_id'] # 获取 user_id
username = session['username'] || 'Guest' # 默认值
删除数据
- 删除单个键:
session.delete('user_id')
- 删除整个 Session:
session.delete
关闭 Session
确保数据写入存储:
session.close
说明:使用块方式创建 Session 会自动关闭,无需手动调用 close
。
4. 完整示例
以下是一个 CGI 脚本,通过 Session 保存和显示用户登录信息。
#!/usr/bin/ruby
require 'cgi'
require 'cgi/session'
cgi = CGI.new
begin
# 创建 Session,存储在 /tmp 目录
session = CGI::Session.new(cgi, 'database_manager' => CGI::Session::FileStore, 'tmpdir' => '/tmp')
# 获取或设置用户名
username = cgi['username'] || session['username'] || 'Guest'
session['username'] = username unless cgi['username'].empty?
cgi.out do
cgi.html do
cgi.body do
cgi.h1 { "Welcome, #{CGI.escapeHTML(username)}!" } +
cgi.form('action' => '/cgi-bin/session.rb', 'method' => 'post') do
cgi.p { "Username: " + cgi.text_field('username') } +
cgi.submit('Login')
end +
cgi.p { "Last username: #{CGI.escapeHTML(session['username'] || 'None')}" }
end
end
end
rescue => e
cgi.out('status' => '500') do
"<html><body><p>Error: #{CGI.escapeHTML(e.message)}</p></body></html>"
end
ensure
session.close if session
end
保存:保存为 /usr/lib/cgi-bin/session.rb
,设置可执行权限(chmod +x
)。
访问:http://localhost/cgi-bin/session.rb?username=Alice
。
功能:
- 通过表单或 URL 参数提交用户名。
- 使用 Session 保存用户名,跨请求保持状态。
- 显示欢迎消息和上一次的用户名。
输出(示例):
<html>
<body>
<h1>Welcome, Alice!</h1>
<form action="/cgi-bin/session.rb" method="post">
<p>Username: <input type="text" name="username"></p>
<input type="submit" value="Login">
</form>
<p>Last username: Alice</p>
</body>
</html>
5. Session 存储方式
CGI::Session
支持多种存储方式:
CGI::Session::FileStore
(默认):将 Session 数据存储为文件。- 文件路径:
tmpdir/prefix + session_id
。 - 示例:
/tmp/cgi_sess_abc123
。 CGI::Session::MemoryStore
:存储在内存中(进程结束时丢失)。
session = CGI::Session.new(cgi, 'database_manager' => CGI::Session::MemoryStore)
- 自定义存储:实现
new
,restore
,update
,close
,delete
方法。
注意:FileStore
适合大多数场景,但需确保 tmpdir
目录可写。
6. 管理 Session 生命周期
- Session ID:自动生成(随机字符串),存储在 Cookie 中(默认键
_session_id
)。 - 过期时间:通过 Cookie 的
expires
设置。
session = CGI::Session.new(cgi, 'session_expires' => Time.now + 3600)
- 删除 Session:
session.delete
cgi.out('cookie' => [CGI::Cookie.new('name' => '_session_id', 'value' => '', 'expires' => Time.now - 3600)]) do
"<p>Session cleared!</p>"
end
7. 注意事项
- 安全性:
- 使用
CGI.escapeHTML
转义用户输入,防止 XSS。 - 不要在 Session 存储敏感数据(如密码),推荐加密或使用安全的会话管理库。
- 启用
secure: true
和httponly: true
保护 Cookie。ruby cookie = CGI::Cookie.new('name' => '_session_id', 'value' => session.session_id, 'secure' => true, 'httponly' => true)
- 文件存储:
- 确保
tmpdir
目录可写(chmod 700 /tmp
)。 - 定期清理过期 Session 文件,防止磁盘占满。
- 性能:CGI 每次请求启动新进程,Session 文件 I/O 可能影响性能,复杂应用推荐 Rails/Sinatra。
- 调试:
- 检查 Session 文件(
/tmp/cgi_sess_*
)。 - 查看 Web 服务器日志(如
/var/log/apache2/error.log
)。 - 现代替代:Rails 的
session
或 Sinatra 的会话管理更高效,支持 Redis、Memcached 等存储。
8. 综合示例:登录计数
以下示例使用 Session 记录用户访问次数。
#!/usr/bin/ruby
require 'cgi'
require 'cgi/session'
cgi = CGI.new
begin
session = CGI::Session.new(cgi, 'database_manager' => CGI::Session::FileStore, 'tmpdir' => '/tmp')
session['visit_count'] = (session['visit_count']&.to_i || 0) + 1
cgi.out do
cgi.html do
cgi.body do
cgi.h1 { "Visit Count: #{session['visit_count']}" } +
cgi.p { "Session ID: #{CGI.escapeHTML(session.session_id)}" }
end
end
end
rescue => e
cgi.out('status' => '500') do
"<html><body><p>Error: #{CGI.escapeHTML(e.message)}</p></body></html>"
end
ensure
session.close if session
end
保存:保存为 /usr/lib/cgi-bin/visit.rb
,设置权限(chmod +x
)。
访问:http://localhost/cgi-bin/visit.rb
。
功能:每次访问增加计数,显示 Session ID。
9. 总结
Ruby 的 CGI::Session
类通过服务器端存储(默认文件存储)和 Cookie(Session ID)实现会话管理。支持存储、读取、删除数据,适合简单的状态保持需求。结合 CGI.escapeHTML
和安全 Cookie 设置可防止常见安全问题。尽管 CGI::Session
简单易用,但性能和扩展性有限,现代 Web 开发推荐使用 Rails 或 Sinatra 的会话管理。
如果你需要更复杂的 Session 操作(如数据库存储、会话加密)或有其他问题,请告诉我!