前言
开发项目都是使用的前后端分离的项目,而我们在使用前后端分离项目去调接口的时候都会产生这个跨越的问题,但这主要还是因为浏览器出于这个安全策略的考虑,帮我们做了这个同源策略。
为什么会产生跨域
跨域问题是由浏览器的同源策略引起的。同源策略是浏览器的一种安全策略,它限制了一个源(协议、域名、端口)的文档或脚本如何与另一个源的资源进行交互。
如果两个页面的协议、域名和端口都相同,则它们属于同一个源,可以自由地进行交互。但是,如果它们的协议、域名或端口之一不同,则它们属于不同的源,就会出现跨域问题。
URL | 说明 | 是否允许通信 |
---|---|---|
www.demo.com/a.js www.demo.com/b.js www.demo.com/lab/c.js | 同一域名,不同文件或路径 | 允许 |
www.demo.com:8000/a.js www.demo.com/b.js | 同一域名,不同端口 | 不允许 |
www.demo.com/a.js www.demo.com/b.js | 同一域名,不同协议 | 不允许 |
www.demo.com/a.js http://127.0.0.1/b.js | 域名和域名对应相同ip | 不允许 |
www.demo.com/a.js x.demo.com/b.js demo.com/c.js | 主域相同,子域不同 | 不允许 |
www.demo1.com/a.js www.demo2.com/b.js | 不同域名 | 不允许 |
如何解决跨域
CORS
CORS 是最常用的解决方案,它允许服务器指定哪些源可以访问其资源,并在响应中添加一些特殊的 HTTP 头部来告知浏览器。这些头部包括:
Access-Control-Allow-Origin
:指定允许访问该资源的源。可以设置为单个源、多个源或*
(表示允许任何源访问该资源)。Access-Control-Allow-Methods
:指定允许的 HTTP 方法。Access-Control-Allow-Headers
:指定允许的 HTTP 头部。Access-Control-Expose-Headers
:指定哪些 HTTP 头部可以暴露给客户端。Access-Control-Max-Age
:指定预检请求的缓存时间。
spring boot 2.7.14配置
java
public class CorsConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); // 允许任何域名使用
corsConfiguration.addAllowedHeader("*"); // 允许任何头
corsConfiguration.addAllowedMethod("*"); // 允许任何方法(post、get等)
corsConfiguration.setMaxAge(3600L);// 缓存时间
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig()); // 对接口配置跨域设置
return new CorsFilter(source);
}
}
1、CORS 解决跨域带来的新问题 预检查请求
CORS 解决跨域问题时会出现预检请求(Preflight Request),这是因为浏览器在发送跨域请求前会先发送一个预检请求,以确定服务器是否允许跨域请求。
预检请求是一个 HTTP OPTIONS 请求,它包含一些特殊的头部,例如 Access-Control-Request-Method
和 Access-Control-Request-Headers
。服务器在收到预检请求后,会检查这些头部,并返回一个包含一些特殊的头部,例如 Access-Control-Allow-Origin
和 Access-Control-Allow-Methods
的响应。如果服务器允许跨域请求,则浏览器会发送实际的跨域请求,否则会报错。
预检请求的目的是为了防止跨域请求对服务器造成安全威胁。通过预检请求,服务器可以检查请求是否合法,并决定是否允许跨域请求。虽然预检请求会增加一些额外的开销,但它可以提高跨域请求的安全性。
要减少预检请求的次数,可以在服务器端设置 Access-Control-Max-Age
头部,指定预检请求的缓存时间。在缓存时间内,浏览器不会再发送预检请求,而是直接发送实际的跨域请求。
缺点
- 多次请求后端 造成压力和增加耗时
2、jsonp
SONP(JSON with Padding)是一种跨域技术,它利用了 HTML 中 <script>
标签可以跨域加载资源的特性。
JSONP 的原理是,在客户端页面中创建一个 <script>
标签,将跨域 URL 作为其 src
属性值,同时在 URL 中指定一个回调函数名,例如:
纯文本复制
xml
<script src="http://example.com/data.php?callback=handleData"></script>
服务器接收到请求后,会将数据包装在回调函数中返回,例如:
erlang
handleData({"name": "John", "age": 30});
客户端页面中定义一个名为 handleData
的函数,当服务器返回数据时,该函数会被自动调用,从而实现跨域数据的获取和处理。
JSONP 的优点是简单易用,不需要特殊的服务器端支持,适用于简单的跨域数据获取场景。但是,JSONP 的缺点也很明显,它只支持 GET 请求,不支持 POST 等其他请求类型,也存在安全隐患,因为回调函数名是由客户端指定的,存在被恶意利用的风险。
总之,JSONP 是一种简单易用的跨域技术,但是存在一些限制和安全隐患,应根据具体情况选择合适的跨域方案。
3、nginx代理
通过nginx配置一个代理服务器(域名与demo1相同,端口不同)做跳板机,反向代理访问demo2接口,并且可以顺便修改cookie中demo信息,方便当前域cookie写入,实现跨域登录。
text
#proxy服务器
server {
listen 81;
server_name www.demo1.com;
location / {
proxy_pass http://www.demo2.com:8080; #反向代理
proxy_cookie_demo www.demo2.com www.demo1.com; #修改cookie里域名
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.demo1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}
location / {
proxy_pass http://www.demo2.com:8080; #反向代理
proxy_cookie_demo www.demo2.com www.demo1.com; #修改cookie里域名
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.demo1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
4、前端跨域处理
我们可以通过配置代理服务器来实现跨域请求。通过设置一个代理服务器将前端的请求转发到目标服务器上,绕过浏览器的同源策略限制,从而解决跨域问题。这种方法对于前端开发人员来说比较便捷,无需对后端进行任何修改。
在一般的前端项目中,我们可以使用工具如webpack-dev-server
、http-proxy-middleware
等来快速设置代理服务器。
java
module.exports = {
// 其他配置项...
devServer: {
proxy: {
// /api表示需要代理的URL路径,可以根据具体情况进行修改
'/api': {
// target表示目标服务器的地址,即需要发送请求的真实服务器地址。
// 开发环境中一般是后端API服务器的地址
target: 'http://example.com', // 目标服务器的地址
// changeOrigin设置为true,会更改请求头中的Host字段为目标URL的主机部分,
// 以便于服务器正确处理请求
changeOrigin: true // 设置为true,将请求头中的Host字段更改为目标URL的主机部分
}
}
}
};
缺点
- 生产前端代码一般会编译成html代码,不会启动一个服务 一般生产也不建议这样做。