1 跨域出现的原因
跨域问题是由于浏览器的同源策略引起的。同源策略是一种安全机制,它限制了一个网页中的脚本只能访问与该网页具有相同协议、域名和端口的资源。如果一个请求的目标资源的协议、域名或端口与当前页面的不同,就会触发跨域请求。跨域问题的出现,一方面是为了保护用户的隐私和数据安全,防止恶意网站通过脚本获取或篡改用户在其他网站上的数据。另一方面,它也有助于确保各个网站之间的安全性,防止恶意网站利用其他网站的资源或执行恶意操作。但是有时候,我们确实需要进行跨域请求,比如在前端开发中使用 Ajax 请求获取其他域名下的数据。为了解决这个问题,出现了一些跨域解决方案,如 JSONP、CORS(跨域资源共享)、服务器代理等。这些解决方案可以使跨域请求变得可行,同时保证了安全
2 同源策略
-
同源策略是浏览器用来约束数据请求的安全机制。同源策略要求:发起请求方和被请求方必须同协议、同域名、同端口,只要有一个不同,且被请求方没有设置CORS策略,那么本次请求会被认为跨域。
-
- 协议:http 和 https
- 域名:www.baidu.com 和 baike.baidu.com
- 端口::8080 和 :8081
需要注意的是,当在跨域 (Cross-Origin) 场景中发送简单请求时,浏览器会使用 CORS (Cross-Origin Resource Sharing) 机制来验证是否允许跨域请求。服务器必须在响应中包含适当的 CORS 标头来授权浏览器进行请求。
3 请求类型
1 简单请求
在Web开发中,HTTP (Hypertext Transfer Protocol) 是一种用于在客户端和服务器之间传输数据的协议。简单请求 (Simple Request) 是其中一种HTTP请求类型。
简单请求是指符合以下条件的请求:
使用以下HTTP方法之一:GET、HEAD、POST。
使用除了自定义标头之外的标准标头。
不发送异步请求,如XMLHttpRequest的withCredentials标志设置为true。
不使用流式传输(Streamed)。
请求的内容类型(Content-Type)为以下之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
简单请求的特点是在发送请求时,浏览器不会发送额外的预检请求(Preflight request)来验证服务器是否允许该请求。相反,浏览器直接发送请求并等待服务器的响应。
需要注意的是,当在跨域 (Cross-Origin) 场景中发送简单请求时,浏览器会使用 CORS (Cross-Origin Resource Sharing) 机制来验证是否允许跨域请求。服务器必须在响应中包含适当的 CORS 标头来授权浏览器进行跨域请求。
2 预检请求
预检请求(Preflight Request)是在某些跨域请求场景下,由浏览器自动发送的一种HTTP OPTIONS请求。预检请求用于在发送实际请求之前,询问服务器是否允许实际请求的跨域访问。
当发起跨域请求并满足以下条件之一时,浏览器会发送预检请求:
使用非简单请求(非简单请求的条件见前一个回答)。
在发送请求时,使用了自定义的标头(例如,使用了不在允许标准标头列表中的标头)。
预检请求的目的是让服务器告知浏览器,是否允许实际的跨域请求。预检请求中的OPTIONS请求会带有额外的头部字段,如Access-Control-Request-Method(指定实际请求使用的HTTP方法)和Access-Control-Request-Headers(指定实际请求中包含的自定义标头)。
服务器接收到预检请求后,会检查请求头部的信息,并在响应中包含适当的CORS头部字段,以指示是否允许实际请求。响应中的CORS头部字段包括:
Access-Control-Allow-Origin:指定允许跨域访问的源。
Access-Control-Allow-Methods:指定允许使用的HTTP方法。
Access-Control-Allow-Headers:指定允许使用的自定义标头。
Access-Control-Max-Age:指定预检结果的缓存时间(以秒为单位)。
只有在服务器返回允许跨域请求的CORS头部字段时,浏览器才会发送实际的请求,否则将不会发送实际请求,并且JavaScript代码无法访问响应数据。
4 如何处理跨域问题
思路就是修改响应头,将响应头中添加浏览器所要求的数据,进而实现跨域
1 全局处理
重写WebMvcConfigurer
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
//是否发送Cookie
.allowCredentials(true)
//放行哪些原始域
.allowedOriginPatterns("*")
.allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
.allowedHeaders("*")
.exposedHeaders("*");
}
}
设置新的 CorsFilter
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
//1. 添加 CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//放行哪些原始域
config.addAllowedOrigin("*");
//是否发送 Cookie
config.setAllowCredentials(true);
//放行哪些请求方式
config.addAllowedMethod("*");
//放行哪些原始请求头部信息
config.addAllowedHeader("*");
//暴露哪些头部信息
config.addExposedHeader("*");
//2. 添加映射路径
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",config);
//3. 返回新的CorsFilter
return new CorsFilter(corsConfigurationSource);
}
}
2 局部处理
注解
@RestController
@CrossOrigin(origins = "*")
public class TransferController {
@RequestMapping("/addRecycleList")
public Result<String> addRecycleList({
// todo
}
}
手动设置
@RequestMapping("/addRecycleList")
@CrossOrigin(origins = "*")
//@CrossOrigin(value = "http://localhost:8081") //指定具体ip允许跨域
public Result<String> addRecycleList({
// todo
}
springBoot处理
import org.springframework.context.annotation.Configuration;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter(filterName = "CorsFilter ")
@Configuration
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin","*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
}