问题:跨域及解决方案
跨域问题
一、跨域问题介绍#
在前后端分离项目中,跨域问题是一定会遇到的。跨域问题的出现,会导致css、js或者ajax对后端请求等资源无法访问的情况。
要想解决跨域问题,首先先得了解什么是跨域问题。
问题:什么是跨域
跨越问题是由于浏览器的同源策略,是浏览器强制要求的安全策略,所以想要了解跨域,首先得了解同源。
所谓同源是指,协议、域名、端口均一致,只要以上三者有一个不一致,就会引发跨域问题
前端页面 | 后端接口 | 是否跨域 | 原因 |
---|---|---|---|
http://banmoon.cn/index.html | http://banmoon.cn/user/get | 否 | 皆一致 |
http://banmoon.cn/index.html | https://banmoon.cn/user/get | 是 | 协议不一致 |
http://banmoon.cn/index.html | http://other.com/user/get | 是 | 域名不一致 |
http://banmoon.cn/index.html | http://banmoon.cn:8080/user/get | 是 | 端口不一致 |
http://localhost/index.html | http://127.0.0.1/user/get | 是 | 域名和IP路径也会导致跨域 |
http://aaa.banmoon.cn/index.html | http://bbb.banmoon.cn/user/get | 是 | 二级域名不一致(cookie也不能访问) |
二、解决跨域问题的方法#
1、jsonp请求#
不做详解,列出优缺点,主要使用cors通信
-
优点:
- 兼容性很好,能在许多低版本的浏览器上运行
-
缺点:
- 只支持get请求,而不支持post及其他请求
- 在调用失败时不会返回各种http的状态码
- 只支持跨域http请求,不能解决跨域访问资源修改的问题
- 安全性,容易被注入
2、CORS通信#
1)介绍#
CORS 是一个 W3C 标准,全称是“跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨域的服务器,发出XMLHttpRequest
请求,从而克服了AJAX只能同源使用的限制。
CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能。
整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与普通的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨域,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感知。因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨域通信。
2)简单请求与非简单请求#
CORS可分为两种请求,简单请求(simple-request)和非简单请求(no-so-simple-request)
-
简单请求
以下条件均满足的为简单请求,否则为非简单请求
情况一:请求方式,为以下其中之一
- get
- post
- head
情况二:请求头,不能出现以下没有的
- Accept
- Accept-Language
- Content-Language
- Content-Type (需要注意额外的限制)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
情况三:Content-Type 的值仅限于下列三者之一
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
如果是简单请求,
- 浏览会在请求的头信息中添加
Origin
,请求直接到后端符文 - 后端服务根据请求头
Origin
(协议 + 域名 + 端口),判断本次请求是否允许跨域 - 后端设置
Access-Control-Allow-Origin
(* 或者 传入的Origin)响应头,表示同意本次请求跨域 - 浏览器识别是否有
Access-Control-Allow-Origin
,如果没有则抛出错误
- 非简单请求
当不满足简单请求的构成条件时,该请求就为非简单请求
如果是非简单请求
-
浏览器会提前发送一个请求方式为
options
的预检请求Origin
:同简单请求一样(协议 + 域名 + 端口)预检请求还包含两个特殊的请求头
Access-Control-Request-Method
:该字段是必须的,本次请求会用到哪些方法Access-Control-Request-Headers
:本次请求额外携带的请求头,多个时用逗号分隔 -
服务器需要进行处理,并成功响应返回成功的状态码,并设置以下响应头
Access-Control-Allow-Origin
:可设置为*,也可设置为具体的请求地址(协议 + 域名 + 端口)Access-Control-Allow-Methods
:必须,表示服务器所支持的所有请求方法,多个时用逗号分隔Access-Control-Allow-Headers
:表示服务端所支持额外的请求头字段,多个时用逗号分隔Access-Control-Allow-Credentials
:布尔值,代表服务端是否同意发送cookieAccess-Control-Max-Age
:本次预检请求的有效期(秒),在此期间浏览器不用再次发送预检请求 -
浏览器判断预检请求回来的响应头信息,判断本次是否允许跨域
-
允许跨域后,发送真正的请求,携带真实的数据进行传输请求
-
如果不允许,则控制台打印报错,不会发送真正的请求
注意:
CORS默认不发送Cookie,想要发送cookie必须如下设置
服务端必须将
Access-Control-Allow-Credentials
响应头设置为true,表示服务端同意传输cookie前端必须设置
withCredentials
,浏览器处理同意传输cookie
// 原生 xml 的设置方式 var xhr = new XMLHttpRequest(); xhr.withCredentials = true; // axios 设置方式 axios.defaults.withCredentials = true; 服务端响应头
Access-Control-Allow-Origin
不能再设置为*,只能是具体的(协议 + 域名 + 端口),与Origin
请求头保持一致新版谷歌浏览器看不见options请求
- 在浏览器地址栏输入
chrome://flags/#out-of-blink-cors
- 将其设置为disabled,然后重启浏览器
三、CORS服务端设置响应#
1、SpringBoot简单设置,@CrossOrigin
注解设置#
对于没有发送cookie要求的,我们可以直接使用@CrossOrigin
注解,加在控制类或者方法上
SpringBoot会自动将Access-Control-Allow-Origin
的值设置为*
@CrossOrigin @RestController public class UserController { @CrossOrigin @PostMapper("/create") public ResultInfo create(@RequestBody User user){ return null; } }
2、SpringBoot简单设置,配置类实现WebMvcConfigurer
接口设置#
此方式可以指定某几个地址访问,比上一种灵活一点
@Configuration public class WebMvcConfiguration implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**")//设置允许跨域的路径 .allowedOrigins("*")//设置允许跨域请求的域名 .allowCredentials(true)//是否允许证书 不再默认开启 .allowedMethods("GET", "POST", "PUT", "DELETE")//设置允许的方法 .maxAge(6000);//跨域允许时间 } }
3、SpringBoot拦截器设置#
此方法灵活程度最高,可以自行设置,允许cookie传输可通过此方法设置
import org.apache.commons.lang.StringUtils; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * CORS的拦截器 */ @Component public class CorsInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String origin = request.getHeader("Origin"); response.setHeader("Access-Control-Allow-Headers", "Sso-Token,token"); response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH"); if(StringUtils.isBlank(origin)){ response.setHeader("Access-Control-Allow-Origin", "*"); }else{ response.setHeader("Access-Control-Allow-Origin", origin); } if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) { response.setStatus(HttpStatus.NO_CONTENT.value()); return true; } return true; } }
/** * 注册使用拦截器 */ @Configuration public class WebMvcConfiguration implements WebMvcConfigurer { @Resource private CorsInterceptor corsInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(corsInterceptor).addPathPatterns("/**"); } }
4、使用Nginx设置响应头达到CORS对服务器的配置#
ngx_http_headers_module
是nginx编译时默认自带的模块,里面有add_header
和expires
等
使用该模块下的add_header
可以完成添加头信息的操作
-
语法:add_ header name value [always];
-
作用:可以添加自定义的响应头信息
-
作用域:可以在http、server、location、if in location中使用
-
注意:
- 当前作用域没有
add_header
,才会继承上一层设置的响应头。也就是说,add_header
可以在最上层统一设置,然后个性化独立设置 - Nginx 1. 7. 5后增加了always语法,即便后端接口发生500错误,设置的响应头也能生效
- 当前作用域没有
-
简单使用
server { listen 80; listen 443 ssl; server_name banmoon.com; add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,bird-idempotency'; add_header 'Access-Control-Allow-Methods' 'GET,PUT,POST,DELETE,OPTIONS'; add_header 'Access-Control-Allow-Credentials' 'true'; location / { proxy_pass localhost:8080; } } -
需要传递cookie时
server { listen 80; listen 443 ssl; server_name banmoon.com; if ($request_method = 'OPTIONS'){ add_header 'Access-Control-Allow-Origin' "$http_origin"; add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,bird-idempotency'; add_header 'Access-Control-Allow-Methods' 'GET,PUT,POST,DELETE,OPTIONS'; add_header 'Access-Control-Allow-Credentials' 'true'; return 204; } if ($request_method != 'OPTIONS'){ add_header 'Access-Control-Allow-Origin' "$http_origin"; add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,bird-idempotency'; add_header 'Access-Control-Allow-Methods' 'GET,PUT,POST,DELETE,OPTIONS'; add_header 'Access-Control-Allow-Credentials' 'true'; } location / { proxy_pass localhost:8080; } }
作者: 半月无霜
出处:https://www.cnblogs.com/banmoon/p/13403260.html
本站使用「CC BY 4.0」创作共享协议,转载请在文章明显位置注明作者及出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!