跨域配置
跨域的特点:
首先要明确跨域的一些特点(有的叫域,有的叫源,我不纠结叫法,能理解跨域就行):
- 跨域是指一个域去请求另一个域的接口
- 域指的是 域名+协议+端口 三者,有一个不一样,就算作是不同的域
- 跨域是浏览器的策略,也就是说没有配置跨域的情况下,使用浏览器前端页面请求接口会被浏览器拦截,使用apifox等工具则能够正常请求
- 当用户使用前端页面调用后端接口时,原域是前端页面所在的服务器的域名+协议+端口(而不是用户的,用户一般也没有),目标域指的是后端api服务器的域名+协议+端口
跨域的流程:
浏览器再跨域请求时执行的操作:
-
发起请求: 用户在浏览器中访问的前端页面(源)中的JavaScript代码通过
XMLHttpRequest
、fetch
API或其他方式尝试向不同域(目标服务器)发起HTTP请求。 - 同源检查: 浏览器首先检查请求的源(当前页面URL)与目标URL是否同源。同源检查包括比较两者之间的协议(HTTP/HTTPS)、域名(完全匹配或子域名匹配)和端口(完全一致)。如果源与目标URL完全相同,即同源,则直接执行下一步;否则,认为这是一个跨域请求,进入跨域处理流程。
-
预检请求(OPTIONS): 对于非简单请求(如使用了自定义头部、PUT/DELETE等非简单方法的请求),浏览器会先发送一个预检(preflight)请求,方法为
OPTIONS
,询问目标服务器是否允许该跨域请求。预检请求的头部包括:-
Origin
: 发起请求的源(即当前页面URL的源) -
Access-Control-Request-Method
: 预期的HTTP方法(如POST
、PUT
等) -
Access-Control-Request-Headers
: 预期发送的自定义头部(如Authorization
、X-Custom-Header
等)
-
-
服务器响应预检请求: 目标服务器接收到预检请求后,根据其CORS配置决定是否允许该跨域请求。如果允许,服务器在响应中返回以下CORS相关头部:
-
Access-Control-Allow-Origin
: 允许的源(可以是单个源、多个源或通配符*
,表示允许所有源) -
Access-Control-Allow-Methods
: 允许的方法(如GET
、POST
、PUT
、DELETE
等) -
Access-Control-Allow-Headers
: 允许的自定义头部 -
Access-Control-Max-Age
: 预检请求结果的缓存时间(单位秒) - 可选的:
Access-Control-Allow-Credentials
(是否允许携带凭据,如Cookie)、Access-Control-Expose-Headers
(允许浏览器访问的响应头部)
-
-
预检请求通过: 浏览器收到预检请求的响应后,检查服务器返回的CORS相关头部是否允许此次跨域请求。如果允许,浏览器缓存预检结果(根据
Access-Control-Max-Age
),然后执行实际的跨域请求。 -
实际请求与响应: 浏览器向目标服务器发送实际的跨域请求。服务器处理请求并返回响应,响应中可能包含以下CORS相关头部:
-
Access-Control-Allow-Origin
: 允许的源 - 可选的:
Access-Control-Allow-Credentials
、Access-Control-Expose-Headers
-
- 浏览器处理响应: 浏览器检查响应中的CORS相关头部,确认是否允许访问响应内容。如果一切符合CORS规则,浏览器将响应数据提供给前端JavaScript代码使用;否则,浏览器阻止JavaScript访问响应数据,并可能抛出错误。
注:如果是简单请求,例如GET请求POST请求且不携带自定义头部,则不发起OPTIONS请求,直接发送原本的请求
后端处理跨域请求:
后端一般有两种方式处理跨域请求,一种是全局的,一种可以精确到controller和接口
全局跨域配置:
在SpringBoot中实现WebMvcConfigurer类的addCorsMappings方法,并在该方法中设置跨域配置
该方法就会处理前端发送来的OPTIONS请求,并且返回给前端是否允许跨域的响应
package com.neu.deliveryPlatform.config;
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 CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// 设置允许跨域的路径
registry.addMapping("/**")
// 设置允许跨域请求的域名
// 这里写的*表示允许所有域,实际情况可能有以下几种形式
// .allowedOrigins(":80"); 写全了,个人认为最正规的写法,但是更喜欢用下面这种
// .allowedOrigins(""); 只写了协议和域名,端口使用http默认的80,https的话是443,应该是最常用的写法(因为一般都使用默认的端口)
// .allowedOrigins("http://123.123.123.123:8888"); 前端服务器没有域名的情况下,也可以使用ip地址
.allowedOriginPatterns("*")
// 是否允许cookie
.allowCredentials(true)
// 设置允许的请求方式
.allowedMethods("GET", "POST", "DELETE", "PUT")
// 设置允许的header属性
.allowedHeaders("*")
// 跨域允许时间,用于设置预检请求(OPTIONS方法)的缓存时间(单位秒)。设置为 3600(即1小时)表示浏览器可以缓存这个CORS响应信息1小时,期间对同一源的跨域请求不再发送预检请求,直接使用缓存结果,从而提高性能。
.maxAge(3600);
}
}
局部跨域配置:
在SpringBoot中使用@CrossOrigin注解可以实现局部跨域配置
可以在controller类上使用,也可以在接口方法上使用(注意同时使用时接口的不会覆盖controller的,而是会叠加起来)
使用该配置的优先级高于全局跨域配置(实现WebMvcConfigurer类的addCorsMappings方法)
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.ResponseEntity;
@RestController
@CrossOrigin(origins = "", methods = "*", maxAge = 3600)
public class MyController {
@GetMapping("/api/public-data")
@CrossOrigin(origins = "*", allowedHeaders = "*", exposedHeaders = "Custom-Header")
public ResponseEntity<?> getPublicData() {
// ...
}
// 其他控制器方法...
}