《ant design pro v5 获取动态菜单与基于角色权限管理视频教程》 和 《零基础学习 Vue JS 3 完全免费视频教程》 正在更新
世界上最伟大的投资就是投资自己的教育

session 原理与实现 (rails 篇)
1. 介绍
上一篇文章cookie原理与实现(rails篇)有说过在rails是如何存取cookie的,而这一篇文章来讲讲rails是如何操作session的。
默认情况下,session是保存在cookie中的。这又是怎么回事呢。众所周知,cookie是存在客户端浏览器中的,把session放在cookie中,可以吗?答案当然是可以。那安全吗?还算安全。就算有人得到了那cookie,它也解密不了,因为存在cookie中的session是通过对称加密处理的,没有密钥是得不到原串的。
rails中关于存储session的配置是这样的:
# config/initializers/session_store.rb
Rails.application.config.session_store :cookie_store, key: '_rails365_session'
所以的session信息,都会存放到cookies中,且key为_rails365_session
。
2. ActionDispatch::Session::CookieStore
实现把session存放到cookie中的功能都依靠这个中间件:ActionDispatch::Session::CookieStore
来看下实现这个功能相关的源码:
# https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
# 得到非加密的session数据,就是原来的session数据
def unpacked_cookie_data(req)
req.fetch_header("action_dispatch.request.unsigned_session_cookie") do |k|
v = stale_session_check! do
if data = get_cookie(req)
data.stringify_keys!
end
data || {}
end
req.set_header k, v
end
end
# session数据包含session_id,值为随机码
def write_session(req, sid, session_data, options)
session_data["session_id"] = sid
session_data
end
# 把session数据存储到cookie中
def set_cookie(request, session_id, cookie)
cookie_jar(request)[@key] = cookie
end
def get_cookie(req)
cookie_jar(req)[@key]
end
来看看unpacked_cookie_data
的实现,它是得到解密后session的数据。
比如下面这样:
env["action_dispatch.request.unsigned_session_cookie"]
{
"session_id" => "760be4b1069ab0c80ccade6d36f00355",
"_csrf_token" => "QdtMjYciHnF8XqSCe0xr8nHo3N5pQdhNeKWhe5ZxOC4=",
"admin" => true
}
或者
session
{
"session_id" => "760be4b1069ab0c80ccade6d36f00355",
"_csrf_token" => "QdtMjYciHnF8XqSCe0xr8nHo3N5pQdhNeKWhe5ZxOC4=",
"admin" => true
}
上面的数据都可以在controller里得到。
3. rack/session/cookie.rb
也就是说,从客户端传给服务器的加密过的cookie已经被先被解密了。
是的,已经先被处理了。
cookie_store.rb
的源码,有一行是这样的:
require 'rack/session/cookie'
是rack程序,我们找到其源码。
# https://github.com/rack/rack/blob/master/lib/rack/session/cookie.rb
def unpacked_cookie_data(request)
request.fetch_header(RACK_SESSION_UNPACKED_COOKIE_DATA) do |k|
session_data = request.cookies[@key]
if @secrets.size > 0 && session_data
digest, session_data = session_data.reverse.split("--", 2)
digest.reverse! if digest
session_data.reverse! if session_data
session_data = nil unless digest_match?(session_data, digest)
end
request.set_header(k, coder.decode(session_data) || {})
end
end
def write_session(req, session_id, session, options)
session = session.merge("session_id" => session_id)
session_data = coder.encode(session)
if @secrets.first
session_data << "--#{generate_hmac(session_data, @secrets.first)}"
end
if session_data.size > (4096 - @key.size)
req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
nil
else
session_data
end
end
def generate_hmac(data, secret)
OpenSSL::HMAC.hexdigest(@hmac.new, secret, data)
end
可见,不仅解密,连加密也是在那个rack程序中实现的。
使用的加密算法是最常用的OpenSSL::HMAC
算法。
这个算法使用的secret或salt是@secrets.first
,@secrets
是在下面这里定义的:
# https://github.com/rails/rails/blob/d50d7094247aad5005cd1b47258ddf338b0dddd7/railties/lib/rails/application.rb#L383
def secrets
@secrets ||= begin
secrets = ActiveSupport::OrderedOptions.new
yaml = config.paths["config/secrets"].first
if File.exist?(yaml)
require "erb"
all_secrets = YAML.load(ERB.new(IO.read(yaml)).result) || {}
env_secrets = all_secrets[Rails.env]
secrets.merge!(env_secrets.symbolize_keys) if env_secrets
end
# Fallback to config.secret_key_base if secrets.secret_key_base isn't set
secrets.secret_key_base ||= config.secret_key_base
# Fallback to config.secret_token if secrets.secret_token isn't set
secrets.secret_token ||= config.secret_token
secrets
end
end
其实就是加载config/secrets.yml
文件取得里面的secret_key_base
参数的值,作为secret。
也就是说,session以cookie的方式存储的加密还是会依赖于secret_key_base
参数的值,所以secret_key_base
是不能泄漏的。
完结。
本站帖子均为原创内容,如需转载请注明出处,谢谢。
© 汕尾市求知科技有限公司 | 关注我们 | 专业版网站 | 在线学员:1121
粤公网安备 44152102000088号 | 粤ICP备19038915号