这里只讨论后端对跨域支持,前端的跨域支持一般都是在测试阶段用用的,跨域还是要后端解决
跨域问题的产生:浏览器的一种安全机制-->同源策略限制
同源策略:URL中包括协议,域名,IP,端口都要完全相同,如果有一项不同,浏览器会觉得有安全风险,不让访问.
前后端分离开发后,这种跨域请求变得突出
W3C 在2014年发布的 RFC 6454 中引入了预检请求的概念,用于在实际请求之前进行预先检查。
浏览器为了支持跨域请求,解决浏览器同源策略出现的问题引入了一种机制---->预检请求
预检请求主要用于确保服务器允许实际请求的发起。服务器在接收到预检请求后,会根据预检请求的信息进行检查,判断是否允许实际请求。如果允许,服务器会在响应头中包含一些 CORS 相关的信息,告诉浏览器可以发送实际请求。这个过程有助于防止恶意网站对其他网站的资源进行滥用。
预见请求就是浏览器自动发出一个查询请求,用来确认目标资源是否支持跨域。
预检请求一定是OPTIONS请求
OPTIONS 请求:
- 用途: 用于在实际请求之前,向服务器发起一个预检请求,以检查服务器是否支持跨域请求。
- 特点: OPTIONS 请求通常包含一些头信息,如
Origin
、Access-Control-Request-Method
、Access-Control-Request-Headers
等,以询问服务器是否允许实际请求。服务器在收到 OPTIONS 请求后,会检查这些头信息,并决定是否允许实际请求。 - 例子: CORS(Cross-Origin Resource Sharing)中的预检请求就是 OPTIONS 请求。
那么什么样的请求,会发送预检请求?
浏览器将请求分类为简单请求和非简单请求
对于简单请求和非简单请求的概念
简单请求:
GET,HEAD,POST请求,但是Content-Type必须是'application/x-www.form-urlencoded'或者'multipart/form-data'或者'text/plain'三者其中之一,没有自定义请求头并且只能有以下一个或者多个请求头包括:Accept,Accept-Language,Content-Language,Content-Type(必须是'application/x-www.form-urlencoded'或者'multipart/form-data'或者'text/plain'三者其中之一),Cache-Control,Expires,Last-Modified,Pragma.
非简单请求:包括PUT,DELETE请求或者包含自定义请求头的其他请求或者Content-Type不是'application/x-www.form-urlencoded'或者'multipart/form-data'或者'text/plain'三者其中之一的请求
一般现在前后端交互都用application/json因此基本都是复杂请求
非简单请求会发送预见请求询问服务器是否允许跨域
演示下非简单请求发送时的情况:
这里浏览器没看到预检请求的发送 可以由于版本问题
后端解决办法1
老版本的Chrome浏览器
做法在chrome地址栏总输入 chrome://flags/#out-of-blink-cors
将其设置为Disabled后重启浏览器
玩了半天 新版本Chrome浏览器的预检请求在Other里,按住Ctrl键+鼠标左点击上面的Fetch/XHR和Other看到预检请求
注意注意,浏览器有缓存,清理缓存,还有后端如果设置了预检请求有效期,那么只有第一次请求才有,后面存活时间内不会发送预检你可能查1天都查不出来,还有注意是否有开启代理,把代理关了(可能影响,只是可能,反正我没影响)
-
ccess-Control-Allow-Origin: 这个头表示允许访问资源的域。如果服务器明确知道请求是来自特定的域,可以设置这个头为对应的域。如果是公开的资源,可以设置为
*
,表示允许所有域的访问。 -
Access-Control-Allow-Methods: 这个头表示允许实际请求使用的 HTTP 方法。如果服务器只允许特定的方法,例如 GET、POST,那么就在这个头中设置相应的方法。
-
Access-Control-Allow-Headers: 这个头表示允许实际请求携带的请求头字段。如果请求中包含的请求头字段在这个头中,浏览器将允许实际请求。通常,如果请求中包含了自定义的请求头,服务器需要设置这个头来允许这些请求头字段。
-
Access-Control-Allow-Credentials: 这个头表示是否允许携带身份凭证(例如 cookies、HTTP 认证信息)进行跨域请求。如果服务器需要接受携带身份凭证的请求,可以设置这个头为 true。注意,如果设置为 true,前端发起请求时需要将
withCredentials
属性设置为 true。 -
Access-Control-Max-Age: 这个头表示预检请求的结果可以被缓存的时间,以减少对服务器的频繁请求。这个头是可选的,不一定要包含。
以上并不是一定要配置,看具体项目需要
通常解决跨域问题不会在每个控制器 控制器上去加@CrossOrign的方式
原因,项目都有过滤器或拦截器,请求还没到控制器就玩个der
行进跨域访问配置有三种方式
1.过滤器 2.拦截器 3.实现WebMvcConfigurer并重写addCorsMappings方法
主要是执行时机
这里用实现WebMvcConfigurer重写addCorsMappings的方法
/** * @author hrui * @date 2023/11/29 4:19 */ @Configuration public class WebConfig implements WebMvcConfigurer { //处理跨域 @Override public void addCorsMappings(CorsRegistry registry) { System.out.println("===========addCorsMappings==========="); registry.addMapping("/**")//选择可以跨域请求的路径 //.addMapping("/api/**") .allowedOriginPatterns("*")//表示允许来自任意域的请求 .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")//允许的实际请求方式,这个不影响预检请求axios.defaults.withCredentials = true;才可以携带Cookie .allowCredentials(true)//是否允许携带凭证信息(Cookie)sessionId 这里测试即使后端允许,前端 .maxAge(3600)//该预检请求的有效时间 单位是秒 有效期内不需要再次发送预检请求 //.allowedHeaders("*");//表示允许所有请求头 .allowedHeaders("token");//这样设置,前端只能携带名为token的自定义请求头,也可以不自定请求头发送预检请求,但是不能定义其他名称的请求头 } }
如果上面这样设置 那么前端请求拦截器
拦截器过滤器参考