(本文最先发表于Zhu’s Blog,未经书面授权许可,任何个人和组织不得以任何形式转载、引用本人的任何文章。本人保留追究侵权者法律责任的权利。)
本文记录阿猪在使用WordPress REST API为跨站应用做身份认证时遇到的一例CORS问题。经过一番折腾,最后发现问题出在使用的REST API Authentication插件在处理HTTP请求时与WordPress原生的REST API有重复。
在WordPress的原生REST API中,支持Cookie Authentication和Basic Authentication两种认证方式。然而目前比较主流的认证方式是Json Web Token(简称JWT)和OAuth2.0,需要借助第三方插件实现。以JWT为例,基本逻辑是使用POST方式向生成token的endpoint发送用户名和密码,如果用户名、密码正确,则返回的Headers中会包含token信息,后续使用REST API时,只要每次在Header中包含Authorization: Bearer <json-web-token>
就可以了。
阿猪最先使用的是miniOrange出品的WordPress REST API Authentication插件。在WordPress中和在本地测试请求生成token都正常,但是在跨站应用中测试时,浏览器的控制台却报错:Access to fetch at 'https://azhu.site/wp-json/api/v1/token-validate' from origin 'https://xxx.azhu.site' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
CORS policy是什么鬼?出于安全考虑,浏览器默认会执行同源策略(Same-origin policy),只有同源(URI、主机名、端口相同)的两个页面才被允许相互访问资源。但是在WEB应用中,跨站访问资源是很常见的需求,这就涉及到“跨源资源分享”(Cross-Origin Resource Sharing)的问题。
经过百度和ChatGPT的一通搜索,大部分的回答是说Nginx的配置不正确,没有开启Access-Control-Allow-Origin
这个Header。按照ChatGPT的指导在Nginx的配置文件中添加了相应的Header信息的配置后,可以正常生成token了。以下代码供参考:
http {...server {...location / {...add_header 'Access-Control-Allow-Origin' 'https://xxx.azhu.site';//将url替换为*表示允许所有访问源add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';...}...}...
}
这里顺便说一下,如果你和阿猪一样使用的是OneinStack的LNMP+WordPress全家桶,那么Nginx的配置文件位置是在/usr/local/nginx/conf/vhost/wordpress.conf
,而不是在/usr/local/nginx/conf/nginx.conf
。
但是还没高兴多久,很快又产生了新的问题。阿猪使用生成的jwt通过GET方式向WordPress REST API请求登录用户信息的时候,浏览器的控制台出现如下错误信息:
Access to fetch at 'https://azhu.site/wp-json/wp/v2/users/me' from origin 'https://xxx.azhu.site' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'https://xxx.azhu.site, https://xxx.azhu.site', but only one is allowed. Have the server send the header with a valid value, or, if an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
再次经过百度和ChatGPT的一通搜索,大部分的回答是说Nginx的配置不正确,有重复的Access-Control-Allow-Origin
配置。但是阿猪翻遍了Nginx目录下的所有conf文件,也没有找到重复的Access-Control-Allow-Origin
配置。如果把仅有的Access-Control-Allow-Origin
配置删除,则又会回到最初的 No 'Access-Control-Allow-Origin' header is present on the requested resource
问题。阿猪又尝试了各种搜索到的办法,但是仍然没有解决。
后来阿猪想,会不会是WordPress的程序中也有响应Headers信息的语句,从而导致了这个问题。毕竟先前验证token的时候使用的也是GET方法,响应是正常的,没有道理同样是GET请求,服务器区别对待。再仔细对比两个endpoint的路径,一个是第三方插件生成的/wp-json/api/v1/token-validate
,一个是WordPress原生的/wp-json/wp/v2/users/me
。可能是第三方插件和WordPress都在同一个HTTP请求中输出了headers信息,从而导致了重复。搜索了一下WordPress的代码,果然有几个文件中包含Access-Control-Allow-Origin
。
阿猪首先尝试更换JWT插件,毕竟如果换个插件能解决,就可以省去改代码的麻烦了。这次阿猪运气好,换了Enrique Chavez的JWT Authentication for WP-API插件后,问题解决了。如果你也正在被这个问题困扰,希望这篇文章能帮助到你。