Cors 在使用Fetch的云函数中未发送/查看授权标头
好像我的头撞到墙上了。。。我已经尝试了多种方法来获取一个简单的Fetch调用来发送授权头。您可以在上看到演示 我收到了许多不同的CORS错误,但最终它们都来自于访问控制允许擦除凭据(变成Cors 在使用Fetch的云函数中未发送/查看授权标头,cors,authorization,google-cloud-functions,fetch,netlify,Cors,Authorization,Google Cloud Functions,Fetch,Netlify,好像我的头撞到墙上了。。。我已经尝试了多种方法来获取一个简单的Fetch调用来发送授权头。您可以在上看到演示 我收到了许多不同的CORS错误,但最终它们都来自于访问控制允许擦除凭据(变成'),或者更简单地说,首先没有授权头 这个设置是一个简单的index.html,托管在Netlify上,后台是一个Google云函数,它应该接收一个令牌,然后将其传回。当然,这是一个更加复杂的设置,但我甚至不能让这个极其简单的版本正常工作。我还通过使用对AWS Lambda函数的近似等效调用验证了该场景,该函数工
'
),或者更简单地说,首先没有授权头
这个设置是一个简单的index.html
,托管在Netlify上,后台是一个Google云函数,它应该接收一个令牌,然后将其传回。当然,这是一个更加复杂的设置,但我甚至不能让这个极其简单的版本正常工作。我还通过使用对AWS Lambda函数的近似等效调用验证了该场景,该函数工作得非常好。该函数前面有一个自动生成的API网关,但我已经指定了一些基线CORS设置
如果调用之前提出过选项方法,则调用res.end()
似乎是一种模式,但我不确定为什么这是一种处理此问题的好方法。尽管如此,我没有得到令牌返回
目前,授权头似乎没有被发送,更不用说在后端接收了
有人看到哪里出了问题吗
请不要建议使用npmcors
软件包,因为如果没有特定的依赖关系,它不会有帮助,也不会做任何无法显式编程的事情
我发现一个相关的问题正在发生
云功能后端
'use strict';
exports.minimalAuthorization = function(req, res) {
const ORIGIN = req.headers.origin;
console.log('ORIGIN', ORIGIN);
const TOKEN = req.headers.Authorization || req.headers.authorization;
console.log('TOKEN', TOKEN);
const METHOD = req.method;
console.log('METHOD', METHOD);
if (req.method === 'OPTIONS') {
res.end();
} else {
res.set('Access-Control-Allow-Origin', ORIGIN);
res.set('Access-Control-Allow-Credentials', 'true');
res.set('Access-Control-Allow-Methods', '*'); // POST, OPTIONS
res.set('Access-Control-Allow-Headers', '*'); // Origin, Content-Type, Accept, Authorization, authorization
if (TOKEN) {
res.status(200).send(JSON.stringify(TOKEN));
} else res.status(400).send(JSON.stringify('Sorry, no token for you...'));
}
};
exports.minimalAuthorization = function(req, res) {
const TOKEN = req.headers.authorization;
console.log('TOKEN', TOKEN);
const METHOD = req.method;
console.log('METHOD', METHOD);
res.set('Access-Control-Allow-Origin', 'https://demo-gcf-auth.netlify.com'); // Your origin here
res.set('Access-Control-Allow-Credentials', 'true');
res.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
// A 204 response (preflight response) happens before actually trying to respond, so accept the auth header and send an OK
if (METHOD === 'OPTIONS') {
res.set('Access-Control-Allow-Headers', 'Authorization');
res.status(204).send('');
} else {
// If it's not an OPTIONS request, actually do send the value/token back
if (TOKEN) {
res.status(200).send(JSON.stringify(TOKEN));
} else res.status(400).send(JSON.stringify('Sorry, no token for you...'));
}
};
相关HTML脚本部分
<script>
const ENDPOINT =
'https://europe-west1-cloud-developer-basics.cloudfunctions.net/minimalAuthorization';
const TOKEN = `eyJhbGciOiJSUzI1NiIsImtpZCI6ImEwYjQwY2NjYmQ0OWQxNmVkMjg2MGRiNzIyNmQ3NDZiNmZhZmRmYzAiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiTWlrYWVsIFZlc2F2dW9yaSIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQUF1RTdtQVhELWtPZHpKbDVDMF9ad3JLY3A3Q2VWWmQzQlp3eG5ydzlicUFTd1UiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vY2xvdWQtZGV2ZWxvcGVyLWJhc2ljcy1lZmUzOSIsImF1ZCI6ImNsb3VkLWRldmVsb3Blci1iYXNpY3MtZWZlMzkiLCJhdXRoX3RpbWUiOjE1NzIyOTc0NTksInVzZXJfaWQiOiJwVERCelM0ZDVyWnJqYjlvMFZLa3g3YmtTZnYyIiwic3ViIjoicFREQnpTNGQ1clpyamI5bzBWS2t4N2JrU2Z2MiIsImlhdCI6MTU3MjI5NzQ2MCwiZXhwIjoxNTcyMzAxMDYwLCJlbWFpbCI6Im1pa2FlbHZlc2F2dW9yaUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnb29nbGUuY29tIjpbIjEwNzUwNDM4MzUwMTA4NDY5ODEzNCJdLCJlbWFpbCI6WyJtaWthZWx2ZXNhdnVvcmlAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoiZ29vZ2xlLmNvbSJ9fQ.LI2ySD6uafnkruEDDmkym6JKoMdhjOEOGKQiytc3SFyeCDERwylqwsmiaCtE7Q6W_FjqrNaAW2rV09rcvuQFPGAMA8uSGiUCdlwau1tBIENHu_HGdW4wI_PWEi6sRmIpbMPTsIPjpcmsSIcpd_WDtz4EldAboXkottFSS7dU81MDbdgrdwKyaq8y-haJqBtr2LAIHy5rg7leSXyY9wqmj9u4iwExWn-pY6BK7dGCEJFTK0_Czvs3qi-0e8bEPmUXwiKzuuMIL_B9l22EHZXqJv0nd9LIzN5_ofyv63U2rG4DbTgNupRAeibhxUO5djVNtgCcFV49618t9ca81d7znQ`;
async function callApiWithToken(token) {
console.log('Calling API with token:', token);
await fetch(ENDPOINT, {
method: 'POST',
credentials: 'include',
headers: {
Authorization: `Bearer ${token}`
}
})
.then(res => res.json())
.catch(error => {
console.error(error);
});
}
callApiWithToken(TOKEN);
</script>
无服务器功能配置,AWS Lambda
functions:
minimalAuthorization:
handler: functions/minimalAuthorization.handler
events:
- http:
method: GET
path: minimalAuthorization
cors:
origin: 'https://demo-gcf-auth.netlify.com'
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
allowCredentials: true
我有点困惑,为什么后端代码将origin设置为请求的origin头。我猜这是出于测试目的,但这意味着所有请求都是有效的
const ORIGIN = req.headers.origin;
这将与设置相同
const ORIGIN = '*';
但是,我通过一些调整成功地使您的代码正常工作:
'use strict';
exports.minimalAuthorization = function(req, res) {
const ORIGIN = req.headers.origin;
console.log('ORIGIN', ORIGIN);
const TOKEN = req.headers.authorization;
console.log('TOKEN', TOKEN);
const METHOD = req.method;
console.log('METHOD', METHOD);
res.set('Access-Control-Allow-Origin', ORIGIN);
res.set('Access-Control-Allow-Credentials', 'true');
res.set('Access-Control-Allow-Methods', '*'); // POST, OPTIONS
if (req.method === 'OPTIONS') {
res.set('Access-Control-Allow-Headers', 'Authorization');
res.status(204).send('');
} else {
if (TOKEN) {
res.status(200).send(JSON.stringify(TOKEN));
} else res.status(400).send(JSON.stringify('Sorry, no token for you...'));
}
};
我认为OPTION方法需要返回2xx响应才能通过预飞
此外,我还将一些标题(如“Access Control Allow Origin”)从非选项响应中移出,并将“Access Control Allow headers”放入选项响应中
我还在codepen上创建了一个前端来测试该功能:
哇,这本应该更加明显。在不同的方面,我看到了始终显式设置内容类型的建议(始终使用
application/json
)的值。这是完全错误的,应该删除,或者将内容类型设置为,例如,text/plain
。这记录在
不太清楚为什么相同的实际HTML会使用AWS Lambda提供功能正常的204+200响应
结束。如果您坚持使用CORS,我将提供前端和后端示例,以便在您需要授权头时为您实现这一点
front
'use strict';
function minimalAuthorization(event, context) {
const TOKEN = event.headers.Authorization.split('Bearer ')[1];
console.log('TOKEN', TOKEN);
const ORIGIN = event.headers.origin;
console.log('ORIGIN', ORIGIN);
if (TOKEN) {
return {
statusCode: 200,
body: TOKEN,
headers: {
'Access-Control-Allow-Origin': ORIGIN,
'Access-Control-Allow-Credentials': true
}
};
} else
return {
statusCode: 400,
body: 'Sorry, no token for you...',
headers: {
'Access-Control-Allow-Origin': ORIGIN,
'Access-Control-Allow-Credentials': true
}
};
}
exports.handler = async (event, context) => {
return minimalAuthorization(event, context);
};
// CORS mode is default, as is Content-Type: 'text/plain'; both are required
await fetch(ENDPOINT, {
credentials: 'include',
headers: {
Authorization: `Bearer ${token}`
}
})
.then(res => res.text())
.then(data => console.log(data))
.catch(error => {
console.error(error);
});
后端
'use strict';
exports.minimalAuthorization = function(req, res) {
const ORIGIN = req.headers.origin;
console.log('ORIGIN', ORIGIN);
const TOKEN = req.headers.Authorization || req.headers.authorization;
console.log('TOKEN', TOKEN);
const METHOD = req.method;
console.log('METHOD', METHOD);
if (req.method === 'OPTIONS') {
res.end();
} else {
res.set('Access-Control-Allow-Origin', ORIGIN);
res.set('Access-Control-Allow-Credentials', 'true');
res.set('Access-Control-Allow-Methods', '*'); // POST, OPTIONS
res.set('Access-Control-Allow-Headers', '*'); // Origin, Content-Type, Accept, Authorization, authorization
if (TOKEN) {
res.status(200).send(JSON.stringify(TOKEN));
} else res.status(400).send(JSON.stringify('Sorry, no token for you...'));
}
};
exports.minimalAuthorization = function(req, res) {
const TOKEN = req.headers.authorization;
console.log('TOKEN', TOKEN);
const METHOD = req.method;
console.log('METHOD', METHOD);
res.set('Access-Control-Allow-Origin', 'https://demo-gcf-auth.netlify.com'); // Your origin here
res.set('Access-Control-Allow-Credentials', 'true');
res.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
// A 204 response (preflight response) happens before actually trying to respond, so accept the auth header and send an OK
if (METHOD === 'OPTIONS') {
res.set('Access-Control-Allow-Headers', 'Authorization');
res.status(204).send('');
} else {
// If it's not an OPTIONS request, actually do send the value/token back
if (TOKEN) {
res.status(200).send(JSON.stringify(TOKEN));
} else res.status(400).send(JSON.stringify('Sorry, no token for you...'));
}
};
使用浏览器devtools中的网络窗格进行检查表明浏览器正在向
https://europe-west1-cloud-developer-basics.cloudfunctions.net/minimalAuthorization
但该预飞失败,因为响应不包括访问控制允许原点响应标头。因此,您当前的服务器代码似乎没有正确处理选项请求-至少它没有导致在对该选项的响应中发送Access Control Allow Origin标头,直到授权请求标头为止,预计浏览器不会将其包含在飞行前选项请求中,因为CORS规范要求浏览器忽略它(无论如何,在该选项请求中,浏览器不会包含您在前端代码中设置的任何标题)。就授权请求标题而言,预计浏览器不会在飞行前选项请求中包含这一点[…]
对我来说是有趣和令人惊讶的,因为(据我所知)如果它是一个选项请求,根据if(req.method=='OPTIONS'){//Send response to OPTIONS requests res.set准确地发送auth头('Access-Control-Allow-Methods','GET');res.set('Access-Control-Allow-Headers','Authorization');[…]
?授权请求标头不会在选项请求中发送。谷歌的示例显示了用于处理选项请求响应的服务器端代码。它不会显示正在发送的授权标头,而是显示了配置为响应选项请求的服务器,例如,“我将允许包含授权标头的请求”。这就是访问控制允许标题:授权响应标题的含义。请检查!正如我自己的回答所示,授权标题没有发送,因为内容类型错误。我看到您使用的是谷歌在上展示的参考实现。我已经尝试过了,现在也尝试过了。但是,使用same结果。日志根本不显示令牌。此外,我只得到了成功的204响应,但没有像您在演示中那样得到200响应。您可以尝试在Netlify托管HTML吗?我开始强烈怀疑Netlify不起作用。关于res.set('Access-Control-Allow-Origin',Origin);
这在功能上与使用“*”
相同,但是当您发送凭据时,星号(AFAIK)会被列入黑名单,并且总是失败。因此,您将其设置为源站时会得到相同的实际结果。这仍然是一种懒惰的方法,但对于测试它应该可以完成这项工作。