最近,我一直在对我维护的gem进行测试重构,我需要测试它是否在正确的时间设置了正确的COOKIE。 但是,gem中使用的COOKIE是已签名的COOKIE,这对我造成了轻微的打ic。 我以前从未测试过签名COOKIE中的值,而且也不是很清楚该怎么做。
所以我想我会分享我发现的内容,以防万一。
Rails上的COOKIE
在Rails应用程序中,可以使用三种COOKIE:
简单会话COOKIE,签名COOKIE和加密COOKIE。 您可以
通过使用控制器中的COOKIEs
对象来设置其中任何一个,如下所示:
class COOKIEsController def indexCOOKIEs[ "simple" ] = "Hello, I am easy to read."COOKIEs.signed[ "protected" ] = "Hello, I can be read, but I can't be tampered with."COOKIEs.encrypted[ "private" ] = "Hello, I can't be read or tampered with."end
end
简单的饼干
简单的COOKIE由纯文本组成。 如果您在浏览器中检查了上面称为“简单”的COOKIE,您将看到文本“您好,我很容易阅读”。
简单的COOKIE可以存储真正无关紧要的数据。 最终用户可以阅读和更改它,并且您的应用程序不会受到影响。
签名的COOKIE
签名的COOKIE不会以纯文本形式发送到浏览器。 取而代之的是,它们由有效载荷和签名组成,并用两个破折号--
分隔。 在破折号之前,有效载荷是基数为64的编码数据 。 要读取数据,您可以基于64位对其进行解码。 此数据不是秘密,但由于COOKIE的第二部分是签名,因此不能被篡改。
通过获取应用程序的secret_key_base
和COOKIE中的数据的HMAC SHA1
摘要来创建签名。 如果您尝试读取COOKIE时更改了COOKIE的内容,则签名将不再与内容匹配,并且Rails将返回nil
。 在ActiveSupport::MessageVerifier
这全部由ActiveSupport::MessageVerifier
处理。 从上面可以看到,您不必担心,可以将COOKIEs.signed
对象当作哈希来对待。
签名的COOKIE对于用户可以读取的数据很有用,但是当您再次将其返回到服务器时,需要信任的是相同的。
加密的COOKIE
加密的COOKIE会更进一步,并加密COOKIE中的数据,然后对其进行签名。 这由ActiveSupport::MessageEncryptor
处理,这意味着如果没有secret_key_base
您将无法读取或写入此COOKIE。 值得庆幸的是,您无需担心加密问题,使用COOKIEs.encrypted
对象,您可以将加密的COOKIE设置为常规哈希来设置。
加密的COOKIE对于要与用户一起存储但不希望它们或任何人读取的私有数据很有用。
测试COOKIE
假设我们现在要测试上面看到的控制器。 我们想
确保我们所有的COOKIE均已正确设置。 测试可能看起来
像这样的东西:
class COOKIEsControllerTest test "should set COOKIEs when getting the index" doget root_urlassert_response :successassert_equal "Hello, I am easy to read." , COOKIEs[ "simple" ]assert_equal "Hello, I can be read, but I can't be tampered with." , COOKIEs[ "protected" ]assert_equal "Hello, I can't be read or tampered with." , COOKIEs[ "private" ]end
end
或使用RSpec Rails:
RSpec.describe COOKIEsController, type: :request doit "should set COOKIEs when getting the index" doget root_urlexpect(response).to have_http_status( :success )expect(COOKIEs[ "simple" ]).to eq( "Hello, I am easy to read." )expect(COOKIEs[ "protected" ]).to eq( "Hello, I can be read, but I can't be tampered with." )expect(COOKIEs[ "private" ]).to eq( "Hello, I can't be read or tampered with." )end
end
但这将在测试签名COOKIE时失败,并且也不会通过加密COOKIE。 如果这些COOKIE已签名或加密,则不能直接从罐子中调用这些COOKIE。
您可能认为您应该针对COOKIE的signed
和encrypted
版本进行测试,如下所示:
assert_equal "Hello, I can be read, but I can't be tampered with." , COOKIEs.signed[ "protected" ]assert_equal "Hello, I can't be read or tampered with." , COOKIEs.encrypted[ "private" ]
那也不行。 至少,如果您使用的是当前推荐的测试控制器的方法(在Minitest中使用ActionDispatch::IntegrationTest
或在RSpec中type: :request
ActionDispatch::IntegrationTest
type: :request
,则它不起作用。
如果您使用的是旧样式ActionController::TestCase
或type: :controller
ActionController::TestCase
type: :controller
测试,则COOKIEs.signed
和COOKIEs.encrypted
将起作用。 如果您的应用程序具有较早的样式测试,请继续阅读以防万一您决定重构它们以使其与当前的Rails方式保持一致。
通过上述测试, COOKIEs
对象实际上是Rack::Test::COOKIEJar
的实例,该实例不了解您的Rails应用程序机密。
那么我们如何测试这些COOKIE?
这是我接触到的宝石的所在。 我需要测试签名COOKIE的结果,但是我有一个Rack::Test::COOKIEJar
对象。 好消息是,我们可以重新使用Rails应用程序自己的ActionDispatch::COOKIEs::COOKIEJar
来解码已签名的COOKIE和解密已加密的COOKIE。
为此,您ActionDispatch::COOKIEs::COOKIEJar
使用测试中的request
对象和COOKIE数据的哈希值来实例化ActionDispatch::COOKIEs::COOKIEJar
的实例。 然后,您可以在该COOKIE罐上调用signed
或encrypted
。
所以现在测试看起来像:
class COOKIEsControllerTest test "should set COOKIEs when getting the index" doget root_urlassert_response :successassert_equal "Hello, I am easy to read." , COOKIEs[ "simple" ]jar = ActionDispatch::COOKIEs::COOKIEJar.build(request, COOKIEs.to_hash)assert_equal "Hello, I can be read, but I can't be tampered with." , jar.signed[ "protected" ]assert_equal "Hello, I can't be read or tampered with." , jar.encrypted[ "private" ]end
end
或规格看起来像:
RSpec.describe COOKIEsController, type: :request doit "gets COOKIEs from the response" doget root_urlexpect(response).to have_http_status( :success )expect(COOKIEs[ "simple" ]).to eq( "Hello, I am easy to read." )jar = ActionDispatch::COOKIEs::COOKIEJar.build(request, COOKIEs.to_hash)expect(jar.signed[ "protected" ]).to eq( "Hello, I can be read, but I can't be tampered with." )expect(jar.encrypted[ "private" ]).to eq( "Hello, I can't be read or tampered with." )end
end
红色,绿色,零食
在本文中,我们看到了如何在Rails中测试签名或加密的COOKIE。 希望您的测试套件运行为绿色,并且现在覆盖您的COOKIE。
我将回到我正在研究的重构中。 现在,这些COOKIE已被删除,还有很多测试可以覆盖。
From: https://hackernoon.com/testing-signed-and-encrypted-COOKIEs-in-rails-application-358z369v