目前,我将前端和后端作为Azure应用服务托管,它们位于不同的子域中.
后端设置为要求客户端证书遵循本指南,我认为这是为Azure应用程序服务执行此操作的唯一方法:
https://docs.microsoft.com/en-us/azure/app-service/app-service-web-configure-tls-mutual-auth
当前端向后端发出请求时,我将withCredentials设置为true – [根据文档] [1],也应该使用客户端证书.
The XMLHttpRequest.withCredentials property is a Boolean that indicates whether or not cross-site Access-Control requests should be made using credentials such as cookies,authorization headers or TLS client certificates. Setting withCredentials has no effect on same-site requests.
前端的相关代码:
const headers = new Headers({ 'Content-Type': 'application/json' }); const options = new RequestOptions({ headers,withCredentials: true }); let apiEndpoint = environment.secureApiEndpoint + '/api/transactions/stored-transactions/'; return this.authHttp.get(apiEndpoint,JSON.stringify(transactionSearchModel),options) .map((response: Response) => { return response.json(); }) .catch(this.handleErrorObservable);
在Chrome上,这是有效的,当发出请求时,浏览器会提示用户输入证书,并且它会包含在预检请求中,一切正常.
但对于所有其他主要浏览器,情况并非如此. Firefox,Edge和Safari都会使预检请求失败,因为服务器在请求中不包含客户端证书时会关闭连接.
直接浏览到api端点会使每个浏览器都提示用户输入证书,因此我非常确定这与大多数浏览器如何使用客户端证书处理预检请求明确相关.
我做错了什么?或者其他浏览器是否通过在发出请求时不提示输入证书而做错了?
我需要支持除Chrome之外的其他浏览器,所以我需要以某种方式解决这个问题.
我已经看到类似的问题通过后端允许而不是需要证书来解决.唯一的问题是我还没有找到一种方法来实际使用Azure应用服务.它要么需要要么不要求.
有没有人对我如何继续前进有任何建议?
所有这一切的要点,你所看到的差异的原因是Chrome中的一个错误.我在https://bugs.chromium.org/p/chromium/issues/detail?id=775438为它提交了一个错误.
问题是Chrome不符合规范要求,这要求浏览器不在预检请求中发送TLS客户端证书;因此,Chrome会在预检中发送您的TLS客户端证书.
Firefox / Edge / Safari遵循规范要求,不在预检中发送TLS客户端证书.
更新:在问题编辑中添加的Chrome屏幕截图显示了对GET请求的OPTIONS请求以及后续的GET请求 – 而不是代码中的POST请求.所以问题可能是服务器禁止POST请求.
https://i.stack.imgur.com/GD8iG.png中显示的请求是CORS preflight OPTIONS
request,浏览器在您的代码中尝试POST请求之前自动发送.
您的代码添加的Content-Type:application / json请求标头是触发浏览器进行预检OPTIONS请求的内容.
重要的是要理解浏览器在预检OPTIONS请求中从不包含任何credentials – 因此发送请求的服务器必须配置为不需要对/ api / transactions / own-transactions /的OPTIONS请求的任何凭证/认证.
但是,从https://i.stack.imgur.com/GD8iG.png开始,服务器似乎禁止对/ api / transactions / own-transactions /的OPTIONS请求.也许这是因为请求缺少服务器期望的凭证,或者可能是因为服务器配置为禁止所有OPTIONS请求,无论如何.
因此,结果是,浏览器断定预检不成功,因此它停在那里并且从未继续尝试从代码中发出POST请求.
考虑到https://i.stack.imgur.com/GD8iG.png中显示的内容,很难理解这在Chrome中是如何实现预期的那样 – 特别是考虑到没有浏览器在预检请求中发送任何类型的credentials,因此任何可能的浏览器在处理凭证方面的差异都没有区别.就预检而言.