原文:https://mp.weixin.qq.com/s/4zJBMNEntwjqAfN6A6diUA
什么是同源策略、跨域
跨域问题是指在浏览器中,当一个网页向不同域名、不同端口或不同协议的资源发起请求时,会受到限制。这是由浏览器的**同源策略(Same-Origin Policy)**所导致的安全限制。
同源策略规定,浏览器允许页面中加载的脚本只与其来源相同的资源进行交互,包括协议(http或https)、主机/域名(domain)、端口(port)
。
同源策略的主要目的是保护用户的隐私和安全,防止恶意网站通过脚本获取用户的敏感信息或执行恶意操作。如果没有同源策略的限制,恶意网站就可以通过脚本来读取用户在其他网站上的数据,或者在用户不知情的情况下执行一些危险的操作。
如果两个页面的协议、主机或端口有任何一个不同,就被认为是跨域请求,浏览器就会阻止页面获取到其响应。
跨域请求示例
通常情况下,在跨域请求当中,涉及到两个请求过程,第一次是预检请求(OPTIONS请求),用于验证服务器是否允许跨域请求,服务器会在响应中携带头部信息给浏览器,浏览器再发送第二次实际的跨域请求,然后服务器接收请求后再进行处理,并在响应中包含必要的头部信息。
注意:在一些特殊情况下,如果服务器已经配置允许来自所有源的请求(使用通配符 *),或者服务器与客户端位于同一源(同源请求),那么只会发送一次实际请求而不需要预检请求。但这通常不是最佳的安全做法,因为它可能会导致安全漏洞。
为什么127.0.0.1和localhost之间算跨域?
经过上面的了解,我们再回到刚开始的问题,为什么127.0.0.1和localhost之间算跨域?
实际就是浏览器的同源策略导致的。虽然它俩指向的都是同一个 IP 地址,即本地主机。
但从浏览器的角度来看,它们仍然被视为不同的域。这是因为浏览器在处理跨域请求时,会将协议+域名+端口号
作为标识符。很显然比如http://localhost:3000/
、http://127.0.0.1:3000/
,它们的主机/域名(domain)
是不一样的,因此会出现跨域。
如何解决跨域问题
法1:修改前端的请求URL
首先针对于上述出现的127.0.0.1
和localhost
之间的跨域,最简单的办法就是将前端设置的请求url修改为localhost
即可。如下是修改之后进行测试的结果:
法2:spring提供的简化跨域配置的机制
Spring框架提供了一些简化跨域配置的方法,主要是通过Spring Web模块(通常与Spring Boot结合使用)来实现的。
1、使用@CrossOrigin
注解:在Controller层,你可以为特定的处理方法或整个控制器类添加@CrossOrigin
注解,以声明允许的跨域请求。
2、使用全局配置:如果要在整个应用程序中配置跨域规则,可以使用全局配置。通过CorsRegistry
来配置全局跨域规则。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("**") // 设置路径匹配模式
.allowedOrigins("http://localhost:8080") // 允许的来源
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的HTTP方法
.allowedHeaders("Origin", "Content-Type", "Accept") // 允许的HTTP头
.allowCredentials(true) // 支持用户凭证
.maxAge(3600); // 缓存时长
}
}
在上面的示例中,我们创建了一个名为WebConfig的配置类,使用addCorsMappings方法定义了CORS规则,允许来自https://localhost:8080
的跨域请求,并指定了允许的HTTP方法、请求头和缓存时间。
通过Nginx反向代理
在分布式项目当中,我们也可以通过Nginx反向代理来解决跨域问题:
在配置文件当中进行配置:
# 设置 HTTP 服务器
server {
listen 80;
server_name localhost;
# 配置反向代理,将 localhost:8080 的请求转发到 localhost:8081
location / {
proxy_pass http://localhost:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# 添加跨域头信息
add_header 'Access-Control-Allow-Origin' 'http://localhost:8080';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
# 处理 OPTIONS 请求的头信息
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
}
}
目前,跨域问题的解决方案有多种选择,但其中CORS(跨域资源共享)和服务器端代理是最常用的,它们具备良好的灵活性和安全性,适用于许多不同的应用场景。不过尽管有多种可行的方法,但我们仍然需要根据具体的情况来选择最合适的解决方案,以确保跨域问题能够得到有效解决。
总结
- 跨域的必要性和重要性:跨域的必要性和重要性在于保护用户数据和隐私、维护互联网生态系统的安全和稳定。
- 合适的跨域解决方案:根据具体的需求和环境,选择合适的解决方案来确保跨域请求的安全性、兼容性、功能性、性能和维护性。
- 分布式系统当中的跨域:跨域在分布式系统当中尤为常见,分布式系统通常由多个独立部署的服务或模块组成,这些服务可能运行在不同的域名、主机或端口上。当一个服务需要与另一个服务或模块进行通信时,就会涉及到跨域请求。这里大家可以入手我的分布式课程,里面内容丰富,能够帮助大家深入分析和解读代码当中的内在逻辑。