Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

跨域问题

  1. 跨域:即同源策略,浏览器的安全机制,要求请求的 协议(http/https)、域名、端口 必须完全一致,否则触发跨域限制
请求地址 目标地址 是否跨域 原因
http://a.com http://a.com/api ✕ 同域 协议、域名、端口均相同
http://a.com:8080 http://a.com:3000 ✔ 跨域 端口不同
http://a.com https://a.com ✔ 跨域 协议不同(http vs https)
http://a.com http://b.com ✔ 跨域 域名不同

如我们自己写项目是在前端用vite创建项目时会提供一个本地开发服务器地址 http://localhost:5173
后端如tomcat会提供一个后端的接口地址如:http://localhost:8080
当我们前端发送向后端发送请求的时候如:

1
2
3
4
5
fetch('http://localhost:8080/user/checkUsernameUsed?username=zhangsan')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

这时候就会引起跨域因为他们的端口不通一个是5173一个是8080破坏了同源机制中的端口号一致。此时就会报类似的错,当然前提是后端没有配置cros头

2.解决方法:这个报错显示后端未正确设置Access-Control-Allow-Origin 响应头,导致浏览器拦截了跨域请求。此时呢我们后端就可以专门写一个过滤器来过滤前端的请求给他加上响应头代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@WebFilter("/*")//拦截所有进入应用的 HTTP 请求。
//当前端和后端部署在不同的域名下时,浏览器会因为同源策略限制而无法直接访问后端资源。
//通过这个过滤器,后端可以显式地告诉浏览器哪些跨域请求是被允许的,从而解决跨域问题。
public class CrosFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse)servletResponse;
HttpServletRequest request =(HttpServletRequest) servletRequest;
//这里在请求头中加入了前端服务器端口
response.setHeader("Access-Control-Allow-Origin", "http://localhost:5173");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, HEAD");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "access-control-allow-origin, authority, content-type, version-info, X-Requested-With");
// 非预检请求,放行即可,预检请求,则到此结束,不需要放行
if(!request.getMethod().equalsIgnoreCase("OPTIONS")){
filterChain.doFilter(servletRequest, servletResponse);
}
}
}

这样就可以啦!
3. 原理:浏览器自动处理:当检测到跨域请求时,浏览器会自动添加 Origin 请求头,并根据服务器在响应头中放的 Access-Control-Allow-Origin判断是否相等决定是否放行。

  • 前端向后端发送请求并附带上自己的Origin 字段(协议 + 域名 + 端口) http://localhost:5173
  • 后端返回在过滤器中设置的响应头中的 Access-Control-Allow-Origin: http://localhost:5173
    和Access-Control-Allow-Methods: GET, POST, OPTIONS # 允许的 HTTP 方法
  • 浏览器验证这两个字段是否相等,相等的话才开始执行js代码,接受后端返回的数据
1
2
3
4
5
6
//假设满足cros
fetch('http://localhost:8080/user/checkUsernameUsed?username=zhangsan')
//放行啦开始执行js代码开始接收数据
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

实际业务中如:
1
fetch('http://localhost:8080/user/checkUsernameUsed?username=zhangsan')
这样的话后端会将 Access-Control-Allow-Origin等放入响应头中,处理的业务数据放入响应体中,所以不管origin和Access-Control-Allow-Origin是否匹配,浏览器都会接受到后端的业务数据,但是不会让js代码执行读取响应体中的数据。 当然使用框架的话就不用手写fliter过滤器来过滤请求了,如:springboot的@CrossOrigin注解配置简化了操作。

评论