OPTIONS 请求得到“401 Unauthorized”响应

Posted

技术标签:

【中文标题】OPTIONS 请求得到“401 Unauthorized”响应【英文标题】:OPTIONS request gets a "401 Unauthorized" response 【发布时间】:2020-04-28 02:34:52 【问题描述】:

我使用 Symfony 4 制作了一个使用自定义令牌验证的 API。我在 Postman 上测试了 API,一切正常,现在我想使用 jQuery 使用 API 并获取所有数据,但在浏览器中,我面临如下 CORS 问题:

从源“http://localhost:8080”访问“http://localhost:8000/api/reports”处的 XMLHttpRequest 已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:它没有 HTTP ok 状态。

这是我的服务器端 API:

我已经实现了CORSEventSubscriber 以允许如下所示的 CORS:

class CORSSubscriber implements EventSubscriberInterface


/**
 * @var TokenStorageInterface
 */
private $tokenStorage;

public function __construct(TokenStorageInterface $tokenStorage)

    $this->tokenStorage = $tokenStorage;

public function onKernelResponse(FilterResponseEvent $event)

    $responseHeaders = $event->getResponse()->headers;
    $responseHeaders->set('Access-Control-Allow-Origin', '*');
    $responseHeaders->set('Access-Control-Allow-Headers', 'x-auth-token, content-type');
    $responseHeaders->set('Access-Control-Allow-Methods', 'POST, GET');


/**
 * @inheritDoc
 */
public static function getSubscribedEvents()

    return [
        KernelEvents::RESPONSE => 'onKernelResponse',
    ];

这是我在控制器中调用的操作:

/**
 * @Route("/api/reports",name="reports",methods="GET","OPTIONS")
 * @param Request $request
 * @return Response
 * @throws  Exception
 */
function getReports(Request $request)

return new JsonResponse('test', Response::HTTP_UNAUTHORIZED);


我尝试像这样使用API

    <script>
    $(document).ready(function()

        authenticate().then(function (data) 
           // the promise resolve the token and data.token output the correct value
           $.ajax(
               url:'http://localhost:8000/api/reports',
               type: 'GET',
                headers: 'X-Auth-Token' : data.token ,
               success: function (data) 
                   //append data to your app
                   console.log(data);
               
           )
        )
    );
    function authenticate() 
        let data=
            "username": "test",
            "password": "test"
        ;
        return new Promise(function(resolve, reject) 
            $.ajax(
                url:'http://localhost:8000/api/auth/check',
                type:'POST',
                data:JSON.stringify(data),
                dataType:'Json',
                success: function (data) 
                    console.log(data);
                resolve(data);
                ,
                error:function () 
                
            )
        );
    


</script>

我添加了这个来仔细调试问题,我发现这个函数只对POST 执行,当有像OPTIONS 这样的令牌方法时它不执行

public function onKernelRequest(GetResponseEvent $event)

    $this->logger->info($event->getRequest()->getRealMethod());

【问题讨论】:

尝试在像 Fiddler 这样的代理中验证实际发送了 auth 标头。 我对代理不是很熟悉,你能给我一个链接吗 telerik.com/fiddler 谢谢我已经能够跟踪请求,但我在 OPTION 请求的标头中找不到设置的令牌,这意味着客户端有问题吗? 它看起来像 - 我猜是时候调试了 【参考方案1】:

经过几天后,我通过添加到 CorsSubscriber 来修复它

public function onKernelResponse(FilterResponseEvent $event)


    $responseHeaders = $event->getResponse()->headers;
    $responseHeaders->set('Access-Control-Allow-Origin', 'http://localhost:8080');
    $responseHeaders->set('Access-Control-Allow-Credentials', 'true');
    $responseHeaders->set('Access-Control-Allow-Headers', ' content-type ,x-auth-token');
    $responseHeaders->set('Access-Control-Allow-Methods', 'POST, GET');
    if($event->getRequest()->getRealMethod()=='OPTIONS')
        $responseHeaders->set('Access-Control-Max-Age', '1728000');
        $event->getResponse()->setStatusCode(200);
    


在处理完响应后,我将200 作为状态码发送,所以我不会有任何CORS 问题

【讨论】:

【参考方案2】:

您正在发出跨域请求并添加非标准标头。这意味着它是Preflighted Request。

浏览器正在发送一个 OPTIONS 请求以请求允许使用自定义标头发出请求。

您无法控制预检请求的格式。您绝对不能为其添加凭据。 (添加凭据是将简单请求转换为预检请求的另一件事。

您需要通过 CORS 标头以权限响应 OPTIONS 请求。由于该请求不会有任何与之关联的凭据,因此您的服务器必须不需要凭据

在请求类型为 OPTIONS 时更改服务器以删除对凭据的要求。


我不知道您使用的服务器端框架的方式,但从您提供的代码推断,我怀疑您应该为 GET 和 OPTIONS 提供单独的路由。 p>

OPTIONS 请求应该与 CORS 相关(并且不获取任何需要授权的数据)。

GET 请求需要授权并返回数据。

【讨论】:

Accept */* Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.9,fr;q=0.8,de;q=0.7 Access-Control-Request-Headers: content-type,x-auth-token Access-Control-Request-Method: GET Connection: keep-alive Host: localhost:8000 Origin: http://localhost:8080 Referer: http://localhost:8080/client.html Sec-Fetch-Mode: cors Sec-Fetch-Site: same-site 这是 OPTION 在标题中得到的内容 @KamelMili — 是的,这是正常的预检请求格式。 并且Access-Control-Request-Headers: content-type,x-auth-token 设置为假设正确,但在调试之后我发现身份验证保护阻止了 OPTION @KamelMili — 是的。这就是为什么您将它应用于 OPTION 请求,就像我说的那样。 感谢您的帮助,您有什么方法可以解决这个问题,如果我理解正确,身份验证守卫将不允许我制作身份验证令牌,因此要么将 OPTIONS 方法列入白名单,要么可能做了看守看怎么解决

以上是关于OPTIONS 请求得到“401 Unauthorized”响应的主要内容,如果未能解决你的问题,请参考以下文章

AJAX中出现两次请求,OPTIONS请求和GET请求

Chrome:POST/OPTIONS 请求因 net::ERR_TIMED_OUT 失败

未经授权的 OPTIONS 请求

CORS 中的POST and OPTIONS 请求

POST 方法自动转换为 OPTIONS

不支持请求方法“OPTIONS”-Spring Boot 应用程序