详解跨域问题
1. 什么是跨域问题?
- 跨域问题是指在Web开发中,当一个网页尝试请求另一个域 (不同的协议、域名、或端口)的资源时,由于浏览器的同源策略限制,这些请求会被阻止。
- 源是什么意思?何为同源 ?
- Web内容的源由用于访问它的URL 的协议,域名和端口。
- 同源不同源一句话就可以判断:就是url中 prtocol host port 都相同即为同源。
- 跨域问题的产生 ?
- 先做一个约定, 你的前端项目部署在 (http://AAA.com)上,你的后端项目部署在(http://BBB.com)上,并且你没有做任何跨域的配置。
- 首先, 你在浏览器访问
http://AAA.com
,前端服务器会将响应的页面返回,浏览器将之渲染绘制,得到页面。 - 如果,在返回的页面中,有一张图片资源,它的url地址是
http://BBB.com/1.jpg
,那么此时这张图片的加载就产生了跨域问题。 - 再者,如果你要点击某个按钮加载一些数据,提交的url是
https://BBB/com/list
,那么这也会产生跨域问题。 - 原因: 身在
http://AAA.com
却想随意访问http://BBB.com
上面的内容,而不经过允许这是不合理的。
- 首先, 你在浏览器访问
- 先做一个约定, 你的前端项目部署在 (http://AAA.com)上,你的后端项目部署在(http://BBB.com)上,并且你没有做任何跨域的配置。
举个例子:
将 http://AAA.com
比喻成你的房间,将 http://BBB.com
比作 你父母的房间。你可以随意使用自己房间里的东西,但是你可以随意使用你父母房间里的东西吗?
###2. 何为预检请求 ? ###
- 预检请求是由浏览器自动发送的一种HTTP OPTIONS请求,它通常用于在发送实际请求之前,询问服务器是否允许实际请求的跨域访问。
- 预检请求与跨域问题息息相关。很多时候,你可以利用预检请求,调试你的跨域配置。
大致请求流程图如下:
3 .解决方法
-
通常解决思路有两种:
- 直接将前端服务和后端服务的协议、域名、端口配置成一样。根据跨域问题的定义,这样就不会产生跨域。
- 配置前端服务器(例如Nginx)支持跨域,配置后端代码支持跨域。
-
从前端解决 (Nginx)
- 在nginx配置文件中,通过添加CORS相关的HTTP头部:
- Access-Control-Allow-Origin: 指定哪些域可以访问资源。可以使用通配符
*
表示允许任何域,但出于安全考虑,通常指定具体的域名。 - Access-Control-Allow-Methods: 指定允许的HTTP方法
- Access-Control-Allow-Headers: 指定允许的自定义请求头部。
- Access-Control-Allow-Credentials: 指定是否允许发送Cookie。
- Access-Control-Allow-Origin: 指定哪些域可以访问资源。可以使用通配符
- 对于预检请求(
OPTIONS
请求),Nginx需要返回状态码204 No Content
,并在响应中包含必要的CORS头部。这告诉浏览器服务器允许跨域请求。
修改配置文件:
- 在nginx配置文件中,通过添加CORS相关的HTTP头部:
server {
listen 80;
server_name example.com;
location / {
# 处理预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
# 处理实际请求
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
}
}
生效配置:
sudo nginx -s reload
-
从后端解决 (Java SpringBoot)
-
配置注解 (
@CrossOrigin
)-
对于控制器或者具体的请求处理方法,你可以使用
@CrossOrigin
注解来允许跨域请求。这种方法比较灵活,可以针对单个控制器或者请求进行配置。 -
import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController @CrossOrigin(origins = "https://example.com") // 允许指定域的跨域请求 public class MyController { @GetMapping("/example") public String example() { return "Example Response"; } }
-
-
全局CORS配置 (实现WebMvcConfigurer)
-
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") // 对所有的URL都生效 .allowedOrigins("https://example.com") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true); } }
-
-
使用CorsFilter.
-
可以创建一个
CorsFilter
Bean来配置CORS。 -
@Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); // 设置允许跨域请求的域名 config.addAllowedOrigin(allowedOrigins); // 是否允许证书 config.setAllowCredentials(true); // 设置允许的方法 config.addAllowedMethod("*"); // 允许任何头 config.addAllowedHeader(allowedHeader); UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(); configSource.registerCorsConfiguration("/**", config); return new CorsFilter(configSource); }
-
-
与SpringSecurity集成
- 如果你的应用使用了Spring Security,CORS配置需要与Spring Security集成。可以通过提供
CorsConfigurationSource
Bean来实现
- 如果你的应用使用了Spring Security,CORS配置需要与Spring Security集成。可以通过提供
-