laravel 授权标头未通过 Codeception API 测试

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28055181/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 10:44:50  来源:igfitidea点击:

Authorization header not making it through in Codeception API testing

phplaravelauthorizationcodeceptionbearer-token

提问by Ben Slinger

I am attempting to test my Laravel 4 REST API using Codeception, but when I try to send through my Authorization header (using the $I->amBearerAuthenticated() function of the REST module) it is not making it through to the eventual request.

我正在尝试使用 Codeception 测试我的 Laravel 4 REST API,但是当我尝试通过我的 Authorization 标头(使用 REST 模块的 $I->amBearerAuthenticated() 函数)发送时,它没有通过最终请求。

From what I can see, the Symfony2 BrowserKit module modifies any headers added into the HTTP_XXX_XXX format, so the header being sent seems to be HTTP_AUTHORIZATION - when I output the received headers in my application, however, neither Authorization nor HTTP_AUTHORIZATION are present.

从我所看到的,Symfony2 BrowserKit 模块修改了添加到 HTTP_XXX_XXX 格式中的任何标头,因此发送的标头似乎是 HTTP_AUTHORIZATION - 然而,当我在我的应用程序中输出接收到的标头时,Authorization 和 HTTP_AUTHORIZATION 都不存在。

If it helps, here is my Codeception test:

如果有帮助,这是我的 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');
}

Headers sent according to BrowserKit (output of $this->client->getInternalRequest()->getServer()in the REST module) :

根据 BrowserKit 发送的标头($this->client->getInternalRequest()->getServer()REST 模块中的输出):

HTTP_HOST   :   localhost
HTTP_USER_AGENT :   Symfony2 BrowserKit
HTTP_AUTHORIZATION  :   Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9sb2NhbGhvc3RcL2F1dGhcL2xvZ2luIiwic3ViIjoxLCJpYXQiOjE0MjE3ODY0NDEsImV4cCI6MTQyMTg3Mjg0MX0.XxxxZMe8gwF9GS8CdKsh5coNQer1c6G6prK05QJEmDQ     
HTTP_REFERER    :   http://localhost/auth/login 
HTTPS   :   false

Headers received according to PHP:

根据 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

The token is received correctly, but my API (correctly) returns a 401 on the GET request because it's not receiving the token.

令牌已正确接收,但我的 API(正确)在 GET 请求中返回 401,因为它没有收到令牌。

Any help would be greatly appreciated!

任何帮助将不胜感激!

采纳答案by Ben Slinger

After some digging into the guts of Laravel and Codeception, I discovered that the problem was with the fact that I was using the Laravel4 module at the same time as the REST module. I hadn't realised that using Laravel4 for HTTP requests actually just simulates the request to the route within the same session, and thus my JWTAuth object was only being resolved out of the IOC container on the first REST call in any particular test. This meant that when making subsequent calls, the request (including headers) from the first call was being retained and thus the Authorization header (which at that point was being passed through with the Request object correctly) was not being seen.

在深入研究 Laravel 和 Codeception 的核心之后,我发现问题在于我在使用 REST 模块的同时使用 Laravel4 模块。我没有意识到对 HTTP 请求使用 Laravel4 实际上只是在同一会话中模拟对路由的请求,因此我的 JWTAuth 对象仅在任何特定测试中的第一次 REST 调用时从 IOC 容器中解析出来。这意味着在进行后续调用时,来自第一次调用的请求(包括标头)将被保留,因此无法看到授权标头(此时与 Request 对象正确传递)。

I was really only using the Laravel4 module to set my environment to 'testing' and to ensure that my filters were running in that environment, so I will now just have to figure out a different way to set that without having to modify my bootstrap/start.php everytime I want to run my tests.

我实际上只是使用 Laravel4 模块将我的环境设置为“测试”并确保我的过滤器在该环境中运行,所以我现在只需要找出一种不同的方法来设置它而无需修改我的引导程序/ start.php 每次我想运行我的测试。

回答by Helder Lucas

I'm using Laravel 5 (no big diff from L4) and REST modules. This is the way I'm doing it right now:

我正在使用 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');
}

Then in my tests:

然后在我的测试中:

// Do stuff ...
$I->amBearerAuthenticated($this->token[0]);
// Do more stuff ...

I'm sure there are better ways to do this, but until I find a better one this is working.

我确信有更好的方法可以做到这一点,但在我找到更好的方法之前,这是有效的。

回答by Mika Tuupola

There is a workaround. Using $I->amHttpAuthenticated("test", "test")makes the Authorization:header permanent. Not sure if this is bug or feature. Instead build the Authorization: Basicheader manually so you can delete it before setting the Authorization: Bearerheader.

有一个解决方法。使用$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);