全网最全的跨域解决方式

373 阅读5分钟

一.跨域

什么是跨域?浏览器为了用户的安全,仅允许向 同域名、同端口 的服务器发送请求。

二.CORS

CORS(Cross-Origin Resource Sharing)是跨域资源共享的缩写,是一种机制,使用额外的HTTP头来告诉浏览器允许一个网页从另一个域请求资源,从而实现服务器和客户端之间的安全跨域通信‌。‌

CORS是一种机制,用于允许跨域请求。在Web应用程序中,由于浏览器的同源策略(Same-Origin Policy),浏览器会限制从一个源(域、协议、端口)发起的HTTP请求,只能访问同一源上的资源。CORS通过在服务器端和客户端之间添加一些额外的HTTP头信息,来解决跨域问题。在Spring Boot中,我们可以使用CORS配置来设置允许跨域访问的规则。

三.如何解决跨域

3.1 nginx

3.1.1 Nginx 解决跨域原理

  1. 跨域问题的原因 跨域问题是由浏览器的 同源策略(Same-Origin Policy) 引起的。同源策略要求: 协议、域名、端口都必须一致。 如果前端和后端运行在不同的域名、IP 或端口上,例如: 前端地址为 http://126.4.3.3 后端地址为 http://126.4.3.3:8080 浏览器会认为它们是不同源,因此会阻止请求,这是跨域问题的本质。
  2. 如何解决跨域问题的配置逻辑 Nginx 配置中,location /api 是关键:
location /api {
    proxy_pass http://127.0.0.1:后端端口;
    proxy_set_header Host $proxy_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_buffering off;
    proxy_set_header Connection "";
}

这里通过 Nginx 的反向代理机制: 将前端通过 /api 路径发起的请求转发到后端(即 http://127.0.0.1:后端端口 )。 对于前端来说,请求的域名和端口仍然是 http://126.4.3.3 ,从浏览器的角度看,请求是同源的。

3.1.2 解决跨域方式

前端

前端请求地址和前端运行地址一致 也就是前端 baseURL 请求 192.168.196.158:8000 具体参考自己运行地址。

后端

需要修改宝塔具体位置

server
{
    listen 80;
    server_name 前端 IP 比如 126.4.3.3;
    root 前端路径;

    location /api {
      proxy_pass  http://127.0.0.1:后端端口;
      proxy_set_header Host $proxy_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_buffering off;
      proxy_set_header Connection "";
    }


    # 这个要写在下面!
    location / {
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
}


BUG

比如,下面这种情况:

所以会导致跨域失败!

解决方法:前端的 baseUrl 前端运行地址

3.2 修改后端服务

3.2.1 配置 @CrossOrigin ]注解

3.2.1.1 controller方法的CORS配置

你可以向@RequestMapping注解处理程序方法添加一个@CrossOrigin注解,以便启用CORS(默认情况下,@CrossOrigin允许在@RequestMapping注解中指定的所有源和HTTP方法):

@RestController
@RequestMapping("/account")
public class AccountController {
 
    @CrossOrigin
    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }
 
    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

其中@CrossOrigin中的2个参数:

  • origins: 允许可访问的域列表
  • maxAge:准备响应前的缓存持续的最大时间(以秒为单位)。
3.2.1.2 为整个controller启用@CrossOrigin
@CrossOrigin(origins = "http://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
 
    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }
 
    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

在这个例子中,对于retrieve()和remove()处理方法都启用了跨域支持,还可以看到如何使用@CrossOrigin属性定制CORS配置。

3.1.2.3 同时使用controller和方法级别的CORS配置

Spring将合并两个注释属性以创建合并的CORS配置。

3.2.2 全局CORS配置

@Configuration
public class WebMvcConfg implements WebMvcConfigurer {
 
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        //设置允许跨域的路径
        registry.addMapping("/**")
            //设置允许跨域请求的域名
            //当**Credentials为true时,**Origin不能为星号,需为具体的ip地址【如果接口不带cookie,ip无需设成具体ip】
            .allowedOrigins("http://localhost:9527", "http://127.0.0.1:9527", "http://127.0.0.1:8082", "http://127.0.0.1:8083")
            //是否允许证书 不再默认开启
            .allowCredentials(true)
            //设置允许的方法
            .allowedMethods("*")
            //跨域允许时间
            .maxAge(3600);
    }
}

模版

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

上述代码中,我们创建了一个名为CorsConfig的配置类,并实现了WebMvcConfigurer接口。在addCorsMappings方法中,我们使用CorsRegistry对象配置了CORS允许的规则。具体而言,我们允许所有的来源(allowedOrigins("*"))、所有的HTTP方法(allowedMethods("*"))、所有的请求头(allowedHeaders("*"))、允许携带身份凭证(allowCredentials(true))并设置了缓存时间(maxAge(3600))。

四. 注意

  1. 直接请求后端端口,那么 Nginx 就失去了反向代理的意义。
  2. 后端配置文件如果写了 samesite 或者 sercure 属性要删除掉!

  1. 后端写了跨域配置要 删除/注释 一般是 @CrossOrigin 或者 CrosConfig
  2. 前端使用域名,但是前端后端使用 ip ,导致 session 设置不上。

解决:前后端统一,要用域名都用域名、IP 都用 IP。

  1. 用 Nginx 跨域就不需要用 Nginx 跨域就不需要,跨域方式不能共存。
  2. 我们现在无法通过浏览器访问接口文档:http://81.69.229.63:8123/api/doc.html。这是因为我们的服务器防火墙没有放开 8123 端口。这里我们故意不放开,因为在之前的部署规划中,后端需要通过 Nginx 进行转发,从而解决跨域问题。

五.小结

几种后端解决跨域的方法:

1)设置 CORS 响应头:后端可以在 HTTP 响应头中添加相关的 CORS 标头,允许特定的源(域名、协议、端口)访问资源。Spring Boot 项目中,可以通过配置 CorsFilter Bean 或者 Web 拦截器(实现 WebMvcConfigurer 接口)实现,不依赖第三方服务。

2)使用代理服务器:可以使用 Nginx 反向代理,通过 add_header 给后端响应添加 Access-Control-Allow-Origin 头,不改代码实现跨域。

3)@CrossOrigin 注解:Spring Boot 项目可以直接在对应的 Controller 或接口方法上添加 @CrossOrigin 注解实现跨域,但这种方式对代码的侵入性较大。

2)使用代理服务器,不需要考虑特定的后端接口实现代码,是一种更通用的解决跨域的方法。