跨域资源共享(CORS)问题与解决方案

跨域资源共享(CORS,Cross-Origin Resource Sharing)是现代web开发中常见且重要的一个概念。它涉及到浏览器的同源策略(Same-Origin Policy),该策略用于防止恶意网站从不同来源窃取数据。然而,在实际开发中,我们经常需要与不同源的资源进行交互,这就引发了跨域问题。本文将详细讨论跨域问题的产生原因、工作流程以及在Spring Boot后端和Axios前端环境中解决跨域问题的方法。

跨域问题的产生原因

同源策略

同源策略是浏览器的一个重要安全机制,用于防止一个网站的脚本与另一个网站的内容进行交互。两个URL被认为是同源的,必须满足以下三个条件:

  1. 协议相同(如http与https不同源)。
  2. 域名相同(如example.com与sub.example.com不同源)。
  3. 端口相同(如http://example.com:80与http://example.com:8080不同源)。

跨域问题

跨域问题产生的原因主要是因为浏览器的同源策略。浏览器限制了从一个源(Origin)加载的文档或脚本与不同源(如不同域、协议或端口)资源的交互。例如,当你的前端应用在http://localhost:3000上运行,而后端API在http://localhost:8080上提供服务时,直接从前端向后端发送请求就会触发跨域问题。

CORS 工作流程

CORS 是一种浏览器技术,它允许服务器明确地表明哪些来源可以访问它的资源。CORS 由一组 HTTP 头部字段组成,这些字段允许服务器描述哪些来源(域、协议和端口)有权限访问服务器上的资源。

简单请求与预检请求

简单请求

简单请求是指那些使用以下方法的请求:

  • GET
  • HEAD
  • POST(带有某些特定类型的Content-Type,如text/plain、multipart/form-data、application/x-www-form-urlencoded)

当浏览器发送一个简单请求时,它会在请求头中包含Origin字段,服务器根据这个字段决定是否允许请求。如果允许,服务器会在响应头中包含Access-Control-Allow-Origin字段。

示例:

前端请求:

javascript 复制代码
axios.get('http://localhost:8080/api/data')
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

后端响应:

http 复制代码
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:3000
Content-Type: application/json

{
  "data": "some data"
}
预检请求

对于复杂请求(如使用PUT、DELETE方法,或自定义头部字段的请求),浏览器会在实际请求之前发送一个OPTIONS请求,这就是所谓的预检请求。预检请求的目的是探测服务器是否允许实际请求。

预检请求的示例:

前端请求:

javascript 复制代码
axios.post('http://localhost:8080/api/data', {data: 'some data'}, {
  headers: {
    'Custom-Header': 'value'
  }
})
.then(response => console.log(response.data))
.catch(error => console.error(error));

浏览器发送的预检请求:

http 复制代码
OPTIONS /api/data HTTP/1.1
Origin: http://localhost:3000
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Custom-Header

服务器响应:

http 复制代码
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Custom-Header

如果预检请求被允许,浏览器会发送实际请求。

Spring Boot中解决CORS问题

在Spring Boot中,我们可以通过多种方式配置CORS策略,允许前端的跨域请求。

使用注解配置CORS

最简单的方式是使用注解@CrossOrigin,可以直接在控制器类或方法上使用该注解。

示例:

java 复制代码
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @CrossOrigin(origins = "http://localhost:3000")
    @GetMapping("/api/data")
    public String getData() {
        return "some data";
    }
}

全局配置CORS

如果需要在整个应用程序中统一配置CORS策略,可以通过实现WebMvcConfigurer接口来配置。

示例:

java 复制代码
import org.springframework.context.annotation.Bean;
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 MyConfig {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**")
                        .allowedOrigins("http://localhost:3000")
                        .allowedMethods("GET", "POST", "PUT", "DELETE")
                        .allowedHeaders("*");
            }
        };
    }
}

通过过滤器配置CORS

还可以通过注册一个CORS过滤器来实现跨域配置,这种方式更灵活,可以更好地控制请求的各个方面。

示例:

java 复制代码
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CorsFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        
        httpServletResponse.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
        httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");

        if ("OPTIONS".equalsIgnoreCase(httpServletRequest.getMethod())) {
            httpServletResponse.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(request, response);
        }
    }
}

Axios配置CORS请求

在前端,我们使用Axios发送HTTP请求。为了处理跨域请求,需要确保Axios正确设置请求头和凭证。

基本使用

基本的GET请求和POST请求已经在前文示例中展示。对于跨域请求,需要特别注意配置withCredentials属性,如果后端需要认证信息(如Cookie)。

示例:

javascript 复制代码
axios.get('http://localhost:8080/api/data', { withCredentials: true })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

自定义请求头

如果需要发送自定义请求头,需要在Axios请求配置中添加相应的头部字段。

示例:

javascript 复制代码
axios.post('http://localhost:8080/api/data', { data: 'some data' }, {
  headers: {
    'Custom-Header': 'value'
  },
  withCredentials: true
})
.then(response => console.log(response.data))
.catch(error => console.error(error));

处理预检请求

为了确保复杂请求的预检请求能成功,需要在后端正确配置Access-Control-Allow-HeadersAccess-Control-Allow-Methods等头部字段。前端无需额外处理,浏览器会自动发送预检请求。

常见问题与解决方案

缺少CORS头部

如果后端响应中缺少必要的CORS头部,会导致浏览器拦截请求。确保服务器响应中包含Access-Control-Allow-Origin等头部字段。

凭证相关问题

对于需要凭证的请求(如使用Cookie),前端需设置withCredentials: true,后端需设置Access-Control-Allow-Credentials: true

动态CORS配置

有时需要根据请求动态配置CORS策略,可以在过滤器或控制器中编写逻辑,根据请求的来源设置相应的头部字段。

示例:

java 复制代码
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;

@RestController
public class DynamicCorsController {

    @CrossOrigin
    @GetMapping("/api/data")
    public ResponseEntity<String> getData(HttpServletRequest request) {
        String origin = request.getHeader("Origin");
        HttpHeaders headers = new HttpHeaders();
        headers.set("Access-Control-Allow-Origin", origin);
        headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
        headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
        return ResponseEntity.ok().headers(headers).body("some data");
    }
}

结论

跨域资源共享(CORS)是现代Web开发中不可避免的问题。理解跨域问题的产生原因和工作流程,对于解决跨域问题至关重要。在Spring Boot后端和Axios前端环境中,通过配置注解、全局配置或过滤器,可以

有效解决CORS问题。本文详细探讨了跨域问题的各个方面,希望能为开发者提供有价值的参考。

参考文献

  1. Mozilla Developer Network (MDN) - Cross-Origin Resource Sharing (CORS)
  2. Spring Framework Documentation - Enabling Cross-Origin Requests for a RESTful Web Service
  3. Axios Documentation - Axios
相关推荐
用户908324602732 天前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
用户8307196840823 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解3 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解3 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记3 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者4 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840824 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解4 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
初次攀爬者5 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺5 天前
搞懂@Autowired 与@Resuorce
java·spring boot·后端