授权标头未通过 Codeception API 测试
Posted
技术标签:
【中文标题】授权标头未通过 Codeception API 测试【英文标题】:Authorization header not making it through in Codeception API testing 【发布时间】:2015-03-19 06:17:11 【问题描述】:我正在尝试使用 Codeception 测试我的 Laravel 4 REST API,但是当我尝试通过我的 Authorization 标头(使用 REST 模块的 $I->amBearerAuthenticated() 函数)发送时,它没有通过最终请求。
据我所见,Symfony2 BrowserKit 模块会修改任何添加到 HTTP_XXX_XXX 格式的标头,因此发送的标头似乎是 HTTP_AUTHORIZATION - 但是,当我在应用程序中输出接收到的标头时,授权和 HTTP_AUTHORIZATION 都不存在.
如果有帮助,这是我的 Codeception 测试:
public function loginAndHitProtectedPage(ApiTester $I)
$I->wantTo('login and successfully get to a protected page');
$I->sendPOST('/auth/login', ['username' => 'user1', 'password' => 'pass']);
$I->seeResponseIsJson();
$token = $I->grabDataFromJsonResponse('token');
$I->amBearerAuthenticated($token);
$I->sendGET('/runs');
$I->seeResponseCodeIs(200);
$I->seeResponseIsJson();
$I->dontSeeResponseContains('error');
根据 BrowserKit 发送的标头(REST 模块中$this->client->getInternalRequest()->getServer()
的输出):
HTTP_HOST : localhost
HTTP_USER_AGENT : Symfony2 BrowserKit
HTTP_AUTHORIZATION : Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9sb2NhbGhvc3RcL2F1dGhcL2xvZ2luIiwic3ViIjoxLCJpYXQiOjE0MjE3ODY0NDEsImV4cCI6MTQyMTg3Mjg0MX0.XxxxZMe8gwF9GS8CdKsh5coNQer1c6G6prK05QJEmDQ
HTTP_REFERER : http://localhost/auth/login
HTTPS : false
根据 php 接收的标头:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Language: en-us,en;q=0.5
Content-Type: application/x-www-form-urlencoded
Host: localhost
Referer: http://localhost/auth/login
User-Agent: Symfony2 BrowserKit
令牌已正确接收,但我的 API(正确)在 GET 请求上返回 401,因为它没有收到令牌。
任何帮助将不胜感激!
【问题讨论】:
我让 PHPStorm 调试为我的测试工作,我在查明这个问题方面取得了一些进展,尽管我仍然有点卡住。看起来我用于授权的 JWTAuth 模块是问题的一部分 - 因为我在同一个测试中进行了两次 REST 调用,所以 JWTAuth 模块似乎只为第一个初始化,并且作为初始化的一部分请求被存储。然后,当进行第二次调用时,将使用第一个请求的标头,因此 Authorization 标头不存在。如果我对令牌进行硬编码并只进行第二个 GET,它就可以工作。继续... 【参考方案1】:我正在使用 Laravel 5(与 L4 没有太大区别)和 REST 模块。这就是我现在的做法:
protected $token;
public function _before(ApiTester $I)
$user = TestDummy(...);
$I->sendPOST('/v1/auth/login', [
'email' => $user->email,
'password' => $user->password
]);
$this->token = $I->grabDataFromResponseByJsonPath('$.token');
然后在我的测试中:
// Do stuff ...
$I->amBearerAuthenticated($this->token[0]);
// Do more stuff ...
我确信有更好的方法可以做到这一点,但在我找到更好的方法之前,这是可行的。
【讨论】:
所以你在所有函数中都调用$I->amBearerAuthenticated($this->token[0]);
?无论如何,我们可以在课堂上做一次并利用它吗?
您可以将其提取到一个 tair 或创建一个基类,然后进行其他扩展【参考方案2】:
有一个解决方法。使用$I->amHttpAuthenticated("test", "test")
使Authorization:
标头永久存在。不确定这是错误还是功能。而是手动构建 Authorization: Basic
标头,以便您可以在设置 Authorization: Bearer
标头之前将其删除。
$I = new ApiTester($scenario);
$I->wantTo("fetch token and use it as bearer");
/* This does not work. */
/* $I->amHttpAuthenticated("test", "test"); */
/* Instead use this. */
$I->setHeader("Authorization", "Basic dGVzdDp0ZXN0");
$I->haveHttpHeader("Content-Type", "application/json");
$I->sendPOST("token", ["read", "write"]);
$I->seeResponseCodeIs(201);
$I->seeResponseIsJson();
$I->seeResponseContainsJson(["status" => "ok"]);
$token = $I->grabDataFromJsonResponse("token");
/* Delete the basic auth header before adding bearer. */
$I->deleteHeader("Authorization");
$I->amBearerAuthenticated($token);
【讨论】:
我认为没有deleteHeader
方法。
但这也有效,而不是使用amHttpAuthenticated
,只需使用两次haveHttpHeader
。
通过$I->haveHttpHeader("Authorization", "Basic $token");
登录好像可以,header真的发送了。
这对我来说就像一个魅力,只是一个小提示,可以直接从代码将凭据转换为 base64:$I->setHeader("Authorization", "Basic".base64_encode($username. ':' . $password));【参考方案3】:
在深入了解 Laravel 和 Codeception 之后,我发现问题在于我同时使用 Laravel4 模块和 REST 模块。我没有意识到将 Laravel4 用于 HTTP 请求实际上只是在同一会话中模拟对路由的请求,因此我的 JWTAuth 对象仅在任何特定测试中的第一次 REST 调用时才从 IOC 容器中解析出来。这意味着在进行后续调用时,第一次调用的请求(包括标头)被保留,因此看不到 Authorization 标头(此时正通过 Request 对象正确传递)。
我实际上只是使用 Laravel4 模块将我的环境设置为“测试”并确保我的过滤器在该环境中运行,所以我现在只需要找出一种不同的方法来设置它而无需修改每次我想运行我的测试时,我的 bootstrap/start.php。
【讨论】:
以上是关于授权标头未通过 Codeception API 测试的主要内容,如果未能解决你的问题,请参考以下文章
未使用 jwt_required 注释时,烧瓶路由请求授权标头