如何从外部Web应用调用Foreman Puppet API并解决CORS跨域报错
你当前代码存在一个典型认知错误:Access-Control-Allow-Origin、Access-Control-Allow-Methods属于服务端返回的响应头,作用是告诉浏览器当前接口允许哪些跨域请求,前端发起请求时手动携带这两个头完全无效,反而会让浏览器将请求判定为非简单请求,强制先发送OPTIONS预检请求。只要服务端没有配置正确的CORS规则,预检请求就会被拦截,触发你看到的报错:
从源
http://localhost:4200向目标地址https://192.168.x.xxx/api/v2/config_reports/1914发起的XMLHttpRequest请求被CORS策略拦截:预检请求响应未通过访问控制校验,请求资源未携带Access-Control-Allow-Origin响应头。
CORS是浏览器内置的安全策略,所有跨域放行规则必须由接口提供方(也就是你部署的Foreman服务)配置,前端单独修改请求代码无法绕过这个限制。
1. 先修正前端错误代码
把请求头里无效的CORS相关字段删掉,修正后的代码如下:
public getReport(){ const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json', 'Authorization': 'Basic ' + btoa('admin:Test123@') }) }; // 注意:该接口返回单个配置报告对象,不是数组,泛型不建议写object[] return this.httpClient.get<object>('https://192.168.8.137/api/v2/config_reports/1914', httpOptions); }
2. 配置Foreman服务端CORS规则
Foreman基于Rails框架开发,原生支持CORS配置,操作步骤如下:
- 登录部署Foreman的服务器,编辑Foreman主配置文件,默认路径为
/etc/foreman/settings.yaml - 在配置文件中添加/修改CORS配置段:
cors: # 允许跨域的前端源,生产环境不要写*,建议写死你的前端实际访问地址 allowed_origins: - 'http://localhost:4200' allowed_methods: - GET - POST - PUT - DELETE - OPTIONS # 允许前端携带的请求头 allowed_headers: - Authorization - Content-Type # 允许前端读取的响应头 expose_headers: - Content-Disposition # 预检请求缓存时间,单位秒 max_age: 600 # 因为要携带Basic认证头,需要开启凭证允许 allow_credentials: true
注意:浏览器规定如果开启了allow_credentials: true,allowed_origins不能配置为通配符*,必须写死明确的前端源地址,否则配置不生效。
- 保存配置后,重启Foreman服务和对应的反向代理服务(如果用了Apache/Nginx做反代):
# 重启Foreman主服务 systemctl restart foreman # 如果用Apache反代就执行systemctl restart httpd,Nginx就执行systemctl restart nginx
如果你的Foreman前面挂了Nginx/Apache反向代理,也可以直接在反代配置层添加对应的CORS响应头,效果和修改Foreman配置一致。
3. 本地开发临时方案(仅调试用)
如果暂时没有权限修改Foreman服务端配置,可以用Angular内置的代理功能绕开浏览器跨域限制:
- 在Angular项目根目录新建
proxy.conf.json文件,写入以下配置:
{ "/api": { "target": "https://192.168.8.137", "secure": false, "changeOrigin": true } }
- 修改
angular.json里的serve配置,指定代理文件:
"serve": { "options": { "proxyConfig": "proxy.conf.json" } }
- 把前端代码里的请求地址改成相对路径
/api/v2/config_reports/1914,重启ng serve即可。这个方案只在本地开发时生效,生产环境必须配置服务端CORS。
内容的提问来源于stack exchange,提问作者Majedur

