SpringBoot 常用跨域处理方案

SpringBoot 常用跨域处理方案

1.什么是跨域?

跨域是浏览器为了保障安全而遵循的一种规则,是同源策略的一部分。

  • 同源:要求协议、域名、端口三者完全相同。
  • 跨域:只要协议、域名、端口中有任何一个不同,浏览器就会判定为跨域请求。

跨域(Cross-Origin)是浏览器独有的安全策略,不存在于安卓、iOS、Node.js、Python、Java 等原生客户端或服务器端环境中。因为浏览器是一个开放的、执行不可信代码,也就是各个网站的 js 脚本的环境,同源策略是为了保证用户的信息安全。

所以,如果平时测试接口用的是 postman 发送请求,不需要关心跨域问题,但是如果是前后端联调就必须处理跨域问题。

2.浏览器处理跨域请求的方式

浏览器遵循同源策略,不允许页面获取跨域请求返回的响应结果。

比如:当前网页是 http://127.0.0.1:63342,然后向服务器 http://127.0.0.1:8080 发送 GET 请求获取数据。整个过程分成三步:

  • 浏览器发送请求
  • 服务器接收请求,处理业务,返回响应
  • 浏览器获取服务器返回的响应并根据响应渲染页面

无论是跨域请求还是非跨域请求,浏览器都可以发送给服务器,并且接收服务器返回的响应数据。

  • 如果该请求是非跨域请求,则 js 脚本可以访问响应数据
  • 如果该请求是跨域请求,浏览器会拦截响应数据,不让 js 访问这些数据

其实浏览器可以向不同的域名发送请求,但是浏览器会拦截响应内容,不让 js 访问,无论请求是否成功。

3.跨域处理方案

跨域处理的核心:让浏览器不要拦截跨域请求返回的数据。

服务器的响应中如果有 Access-Control-Allow-Origin: * 这个响应头,就是告诉浏览器:"我是服务器,虽然我跟这个网页不是同源的,但是我允许这个网页跟我通信,我们之间的通信是安全的",浏览器就不会拦截 js 对响应数据的访问。

浏览器发送的请求可以分为简单请求和复杂请求:

  • 如果是简单请求,则浏览器直接发送。
  • 如果是复杂请求,则浏览器先发送一个预检请求,即 OPTIONS 请求,问一句:"我可以发送一个超级复杂的跨域请求吗?",服务器需要返回针对 OPTIONS 的响应。如果服务器允许发送这个复杂请求,浏览器才会真正发送请求。

常见的跨域处理方案有:代理服务器、后端服务器跨域配置。

3.1代理服务器

浏览器将请求发送到跟页面同源的代理服务器,代理服务器再将请求转发到目标服务器。因为服务器间通信不受同源策略限制。比如常见的用 nginx 作为代理服务器。

请求处理过程:

  • 前端发送请求,请求经过 nginx,请求被转发到后端服务器。

  • 服务器返回原始响应,原始响应经过 nginx,nginx 自动添加 Access-Control-Allow-Origin: * 响应头,响应返回给前端,js 可以访问响应数据。

    nginx.conf

    server {
    listen 63342;
    # 前端页面的域名或ip
    server_name 127.0.0.1;

    复制代码
      # 代理所有以 /api/ 开头的请求到后端服务器
      location /api/ {
          # 后端服务器地址
          proxy_pass http://127.0.0.1:8080/; 
    
          # 修改请求头,确保后端能收到正确的原始主机信息
          proxy_set_header Host $host; 
          proxy_set_header X-Real-IP $remote_addr; 
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
          proxy_set_header X-Forwarded-Proto $scheme;
    
          # 添加CORS响应头,允许所有来源的请求,生产环境应关闭
          add_header 'Access-Control-Allow-Origin' '*'; 
          
          # 允许的请求方法
          add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, PATCH';
          
          if ($request_method = 'OPTIONS') {
              # 对于OPTIONS请求,直接返回204状态码,不需要转发到后端
              return 204;
          }
      }

    }

3.2 后端跨域配置

3.2.1 配置 CORS

配置全局 CORS 规则,在所有响应头都配置可以跨域访问。

java 复制代码
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // 允许所有接口跨域
                .allowCredentials(true) // 允许浏览器在跨域请求中发送认证信息
                .allowedOriginPatterns("*") // 允许访问资源的源(协议、域名、端口)
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的方法
                .allowedHeaders("*") // 允许的请求头
                .exposedHeaders("*"); // 哪些响应头可以暴露给前端js
    }
}

3.2.2 提供 CorsFilter

提供一个 CorsFilter 的 Bean 作为过滤器。

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class MyCorsFilter {
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();

        config.addAllowedOriginPattern("*"); // 放行所有域名,生产环境请对此进行修改

        config.setAllowCredentials(true); // 是否发送cookie

        config.addAllowedMethod("*");  // 放行的请求方式

        config.addAllowedHeader("*"); // 放行的请求头

        config.addExposedHeader("*"); // 暴露头部信息

        // UrlBasedCorsConfigurationSource: 可以为不同的URL路径设置不同的CORS规则
        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        // 所有的URL路径都使用同一个CORS规则
        corsConfigurationSource.registerCorsConfiguration("/**", config);

        return new CorsFilter(corsConfigurationSource);
    }
}

3.2.3 @CrossOrigin 注解

在接口类上或者接口方法上添加 @CrossOrigin 注解,表示整个类、单个接口的响应不会被拦截。

java 复制代码
@RestController
@CrossOrigin
public class DemoController {

    @PutMapping("/put")
    public Integer put(MultipartFile file) {
        System.out.println(file.getOriginalFilename());
        return 200;
    }

    @GetMapping("/get")
    public Integer get() {
        return 200;
    }
}
相关推荐
BD_Marathon1 小时前
【Flink】部署模式
java·数据库·flink
鼠鼠我捏,要死了捏4 小时前
深入解析Java NIO多路复用原理与性能优化实践指南
java·性能优化·nio
你的人类朋友4 小时前
vi编辑器命令常用操作整理(持续更新)
后端
superlls4 小时前
(Redis)主从哨兵模式与集群模式
java·开发语言·redis
胡gh4 小时前
简单又复杂,难道只能说一个有箭头一个没箭头?这种问题该怎么回答?
javascript·后端·面试
一只叫煤球的猫5 小时前
看到同事设计的表结构我人麻了!聊聊怎么更好去设计数据库表
后端·mysql·面试
uzong5 小时前
技术人如何对客做好沟通(上篇)
后端
叫我阿柒啊6 小时前
Java全栈工程师面试实战:从基础到微服务的深度解析
java·redis·微服务·node.js·vue3·全栈开发·电商平台
颜如玉6 小时前
Redis scan高位进位加法机制浅析
redis·后端·开源