我的应用程序有一个使用 Spring Boot 构建的后端,以及一个使用 Angular 构建并由 nginx 提供服务的前端 Web 应用程序。 Nginx 配置为将 /api/ 的请求反向代理到后端:
location /api/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://back:8080;
}
如果 nginx 在默认端口 (80) 上运行,我可以通过 http://myserver/ 访问我的应用程序,不会出现任何问题。 XHR 调用 http://myserver/api/some/REST/resource 工作完美。
如果我将 nginx 更改为侦听其他端口(随机:9043),则所有 XHR 调用都会失败,并显示错误“无效的 CORS 请求”。这是出乎意料的,因为只有我的前端应用程序正在调用 API,并且由于反向代理,该 API 与 javascript 文件在同一主机上提供服务。
为了让它发挥作用,我需要将其添加到我的 Spring 应用程序中:
@Configuration
public class CorsConfig {
@Value("${url.base:''}")
private String urlBase;
@Value("${cors.allowed.origins:[]}")
private String[] allowedOrigins;
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping(urlBase + "/**")
.allowedOrigins(allowedOrigins)
.allowedMethods("*");
}
};
}
}
然后在我的配置中将
cors.allowed.origin
设置为 http://myserver:9043/
。
这可行,但如果我要这样做,那就不切实际了:
是否有解决方案可以通过执行以下任一操作来解决此问题:
编辑:更多细节和示例:
服务已码头化:
仅当前端在端口 80 上暴露于外界时才有效...
在反向请求中添加两个http header:
X-转发-原型
X-转发端口
指示原始协议和端口(与原始请求匹配)。
我也遇到了这个问题(特别是使用 dockerforward),而且我并没有真正控制基础设施方面,因为我的应用程序是一个任何人都可以启动的自托管工具。
我至少检查了 FastAPI/starlett 的 cors 中间件(python)进行比较,服务器端检查似乎并不标准。最后,如果浏览器已经检查源是否与配置的标头匹配,则不需要后端检查,并且仅在非常旧的浏览器的情况下才进行后端检查。如果它不是请求 Origin 标头的浏览器,则无论如何都可以将其设置为任何内容,因此它也不会提供额外的安全性。
Docker 当然不会正确设置转发的端口标头,因为它只是映射流量,因此我只需从原始标头中删除端口,如下所示:
class OriginHeaderFilter : OncePerRequestFilter() {
override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse,
filterChain: FilterChain,
) {
val origin = request.getHeader("Origin")
if (origin != null) {
val originWithoutPort = origin.replace(Regex(":(\\d+)"), "")
val modifiedRequest = object : HttpServletRequestWrapper(request) {
override fun getHeader(name: String): String? =
if (name.equals("Origin", ignoreCase = true)) originWithoutPort else super.getHeader(name)
override fun getHeaders(name: String): Enumeration<String> =
if (name.equals("Origin", ignoreCase = true)) {
Collections.enumeration(listOf(originWithoutPort))
} else {
super.getHeaders(name)
}
}
filterChain.doFilter(modifiedRequest, response)
} else {
filterChain.doFilter(request, response)
}
}
}
然后将其添加到我的过滤器链中,如下所示:
addFilterBefore<CorsFilter>(OriginHeaderFilter())
您甚至可以删除整个 Origin 标头。 请注意,这与完全禁用 CORS 不同,重要的是设置了同源响应标头,以便浏览器可以防止 CSRF 攻击,但据我了解,额外的后端检查是很好的对于现代浏览器来说没有必要(并且在代理的情况下非常容易出错)。
希望这可以帮助其他通过谷歌找到它的人。