authentication - Servant 客户端如何处理收到的 cookie?
问题描述
我想使用 Servant 客户端首先调用登录端点以获取会话 cookie,然后对需要 cookie 身份验证的端点发出请求。
API是(简化的)
import qualified Servant as SV
import qualified Servant.Auth.Server as AS
import qualified Servant.Client as SC
-- Authentication and X-CSRF cookies
type CookieHeader = ( SV.Headers '[SV.Header "Set-Cookie" AS.SetCookie
, SV.Header "Set-Cookie" AS.SetCookie]
SV.NoContent )
type LoginEndpoint = "login" :> SV.ReqBody '[SV.JSON] Login :> SV.Verb 'SV.POST 204 '[SV.JSON] CookieHeader
type ProtectedEndpoint = "protected" :> SV.Get '[SV.JSON]
-- The overall API
type Api = LoginEndpoint :<|> (AS.Auth '[AS.Cookie, AS.JWT] User :> ProtectedEndpoint)
apiProxy :: Proxy Api
apiProxy = Proxy
我将客户端定义如下:
loginClient :: Api.Login -> SC.ClientM Api.CookieHeader
protectedClient :: AC.Token -> SC.ClientM Text :<|> SC.ClientM SV.NoContent
loginClient :<|> protectedClient = SC.client Api.apiProxy
客户端如何处理认证cookie?我可以想到两种方法。在ClientM
monad 中执行请求时,例如
do
result <- SC.runClientM (loginClient (Login "user" "password")) clientEnv
[..]
Login
登录请求正文和clientEnv
类型在哪里Servant.Client.ClientEnv
,cookie 可以是 的一部分result
,它可以在cookieJar
TVar 里面更新clientEnv
,或两者兼而有之。我会假设 TVar 将被更新,以便后续请求clientEnv
将发送接收到的 cookie。但是,我尝试读取 TVar 并使用它检查其内容时Network.HTTP.Client.destroyCookieJar
发现了一个空数组。这是故意的吗?我在文档中找不到任何东西。
因此,要进行经过身份验证的调用,我需要从result
(如何?)中的标头中提取 cookie,更新 TVar,创建一个clientEnv
引用此 TVar 的新对象,并使用此新环境进行经过身份验证的调用。这确实是建议的程序吗?我问是因为我认为用例是如此标准,以至于应该有一个更简化的解决方案。在那儿?我错过了什么吗?
解决方案
经过一些实验,我发现 Servant 客户端确实cookieJar
在clientEnv
. 更准确地说,clientEnv
包含cookieJar
类型为的字段Maybe (TVar CookieJar)
。是客户端根据Set-Cookie
后续请求的指令更新的TVar。在发出第一个请求之前,由开发人员创建和初始化该 TVar;否则,Servant 客户端将在请求之间丢弃 cookie。
此外,还可以使用与请求正文相同的方式来检索 cookie。为此,必须将要检索的 cookie 定义为 API 类型的一部分,就像我原来的问题的示例一样:
type LoginEndpoint = "login" :> SV.ReqBody '[SV.JSON] Login :> SV.Verb 'SV.POST 204 '[SV.JSON] CookieHeader
一开始拆解返回的东西有点棘手,因为我需要弄清楚 Servant 的类型级机器产生的最终类型。最终,我做了以下事情:
SV.Headers resp h <- tryRequest clientEnv (loginClient (Api.Login "user" "pwd"))
let headers = SV.getHeaders h
wheretryRequest
是执行runClientM
和提取Right
零件的助手。模式匹配resp
包含返回值(此处NoContent
),而h
is 是HList
不同标头中的一个。它可以转换成Network.HTTP.Types.Header
使用ServantgetHeaders
函数的常规列表。
cookieJar
然后,可以通过向TVar添加新标头来更改或生成新标头并通过新请求提交它们(请参阅 Network.HTTP.Client 中的cookie 操作函数)。
推荐阅读
- azure-functions - Azure 事件网格订阅 WebHook 验证失败,云事件架构 v1.0
- spring-boot - 在测试 Spring Boot 应用程序时替代 HTMLUnit 作为无头浏览器
- ubuntu - 有没有办法用ffmpeg制作时间流逝?
- strategy-pattern - 策略测试器概述数据提取
- python-3.x - Python3 pop() 函数只删除字符串
- fatal-error - JVM一次又一次地崩溃并出现致命错误
- qt - QML Gridview重叠单元格
- css - 链接css文件/css样式不起作用时的mime错误
- reactjs - 如果我将 `YAxisProps['reversed']` 设置为 true,则不会显示 YAxis 最小值
- sql - 使用带有子查询的 IN 运算符的 SQL Server 似乎是一个 BUG?