Spring: 在SpringBoot项目中解决前端跨域问题

一、什么是跨域问题

跨域问题,也称为跨域资源共享(CORS,Cross-Origin Resource Sharing)问题,主要出现在Web开发中,尤其是当前端代码和后端代码分别部署在不同的域名下时。由于浏览器的同源策略限制,一个域的网页中通过JavaScript发起的跨域请求会被浏览器阻止,除非响应报文包含了正确的CORS相关头部信息。

跨域问题主要涉及到以下几个关键点:

  • 同源与跨源:如果两个页面的协议、域名和端口都相同,则它们是同源的;否则,它们是跨源的。

  • 浏览器限制:浏览器默认会阻止跨源请求,除非后端服务器明确允许这种请求。

  • CORS头部:为了允许跨源请求,后端服务器需要在响应中包含特定的CORS头部,如Access-Control-Allow-Origin。

  • 预检请求:对于某些类型的跨域请求(如带有自定义头部的请求或PUT/DELETE等请求),浏览器会先发送一个OPTIONS请求进行预检,以检查服务器是否允许这种请求。

解决跨域问题的方法有多种,包括但不限于:

  • 使用CORS:在后端服务器上配置CORS策略,允许特定的跨源请求。
  • 使用代理服务器:在前端和后端之间设置一个代理服务器,前端请求发送到代理服务器,再由代理服务器转发到后端服务器。
  • JSONP:一种古老的跨域技术,利用<script>标签没有跨域限制的特性来实现跨域请求。但JSONP只支持GET请求,且存在安全隐患,而且前后端都需要写相应的代码。
  • 设置document.domain:对于主域名相同而子域名不同的情况,可以通过设置document.domain来实现跨域。

在实际开发中,应根据具体需求和场景选择合适的跨域解决方案。

二、浏览器的同源策略

同源策略是浏览器的一个安全功能,它阻止从一个源加载的文档或脚本与另一个源的资源进行交互。这是为了防止恶意文档读取或修改另一个源加载的资源。同源策略限制了不同源之间的DOM访问、AJAX请求以及Web存储访问等。当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。此外浏览器是否启用跨域保护机制是又后端的响应Response来决定的
在这里插入图片描述

三、SpringBoot项目中解决跨域问题的5种方式:使用CORS

1、自定 web filter 实现跨域(全局跨域)

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class MyCorsFilter implements Filter {
    private static Logger LOGGER = LoggerFactory.getLogger(MyCorsFilter.class);
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", "*");

        LOGGER.info("reponse cors: Access-Control-Allow-Origin={}" +
                ", Access-Control-Allow-Methods={}" +
                ", Access-Control-Allow-Credentials={}" +
                ", Access-Control-Allow-Headers={}",
                response.getHeader("Access-Control-Allow-Origin"),
                response.getHeader("Access-Control-Allow-Methods"),
                response.getHeader("Access-Control-Allow-Credentials"),
                response.getHeader("Access-Control-Allow-Headers"));
        chain.doFilter(req, res);
    }
}

2、使用WebMvcConfigurer自定义跨域配置

使用WebMvcConfigurer自定义跨域配置的原理主要基于Spring MVC的扩展机制。WebMvcConfigurer接口是Spring框架为开发者提供的用于自定义Spring MVC配置的一种方式。它包含一系列的方法,允许开发者覆盖或扩展Spring MVC的默认行为。

在跨域配置的场景中,WebMvcConfigurer接口中的addCorsMappings方法特别有用。这个方法允许你定义哪些URL路径应该应用跨域(CORS)策略。你可以为特定的路径指定允许的源(即哪些域名或IP地址可以访问这些路径)、请求方法(如GET、POST等)、请求头以及预检请求的缓存时间等。

当你实现WebMvcConfigurer接口并重写addCorsMappings方法时,Spring MVC会在启动时加载你的配置,并将其应用到相应的请求处理流程中。当有请求到达时,Spring MVC会检查请求的URL是否匹配你定义的跨域映射。如果匹配,Spring MVC就会在响应中自动添加相应的CORS相关的HTTP头(如Access-Control-Allow-Origin、Access-Control-Allow-Methods等),从而允许跨域请求。

这种方式的优点在于它允许你以声明式的方式定义跨域策略,而不需要在每个控制器或方法中单独处理。同时,由于它是基于Spring MVC的扩展机制,因此可以很好地与Spring Boot等Spring框架集成,无需额外的配置或依赖。

需要注意的是,addCorsMappings方法定义的是全局的跨域策略,它会应用到所有匹配的URL路径上。如果你需要为不同的路径定义不同的跨域策略,你可以在addCorsMappings方法中添加多个映射,并为每个映射指定不同的配置。另外,如果你需要更细粒度的控制或更复杂的逻辑,你可能需要使用过滤器(Filter)或拦截器(Interceptor)来实现跨域配置。

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 WebConfig implements WebMvcConfigurer {  
    @Override  
    public void addCorsMappings(CorsRegistry registry) {  
        registry.addMapping("/**") // 允许所有路径  
                .allowedOrigins("http://example.com") // 允许哪些源的请求  
                .allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的HTTP方法  
                .allowedHeaders("*") // 允许的头部  
                .allowCredentials(true) // 是否允许发送cookies  
                .maxAge(168000); // 预检请求的缓存时间(秒)  
    }  
}

3、 CorsFilter(全局跨域)

CorsFilter 是 Java Spring 框架中用于处理跨域请求(CORS,Cross-Origin Resource Sharing)的过滤器。通过使用 CorsFilter,你可以配置你的 Spring 应用以允许跨域请求,从而避免浏览器因同源策略而阻止这些请求。下面是一个简单的 Spring Boot 应用中如何使用 CorsFilter 的例子:

  • 添加依赖(如果你使用 Spring Boot,这个依赖可能已经包含在内):
<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-web</artifactId>  
</dependency>
  • 创建 CorsFilter:
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 CorsConfig {  
  
    @Bean  
    public CorsFilter corsFilter() {  
        CorsConfiguration config = new CorsConfiguration();  
        config.setAllowedOrigins(Arrays.asList("*")); // 允许所有源进行访问  
        config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE")); // 允许的方法  
        config.setAllowCredentials(true); // 是否发送cookie  
        config.setMaxAge(168000); // 预检间隔时间  
  
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();  
        source.registerCorsConfiguration("/**", config); // 对所有路径应用 CORS 配置  
  
        return new CorsFilter(source);  
    }  
}

在这个例子中,我们创建了一个 CorsFilter Bean,它允许来自任何源的请求,并允许 GET、POST、PUT 和 DELETE 方法。我们还设置了 AllowCredentials 为 true,这意味着浏览器可以发送带有凭据(如 cookies、HTTP 认证及客户端 SSL 证明等)的请求。最后,我们设置了 CORS 预检请求的缓存时间为 168000 秒(即 2 天)。

  • 启动你的 Spring Boot 应用。现在,你的应用应该能够处理跨域请求了。请注意,出于安全考虑,你不应该在生产环境中将 AllowedOrigins 设置为 *,除非你完全信任所有可能的源。通常,你会将 AllowedOrigins 设置为一个特定的域名列表。

4、使用@CrossOrigin注解 (局部跨域)

@CrossOrigin 是 Spring Framework 提供的一个注解,用于解决跨域资源共享(CORS)问题。通过在类或者方法上使用 @CrossOrigin 注解,你可以轻松地为 Spring MVC 控制器开启 CORS 支持。这个注解允许你指定哪些域、HTTP 方法以及头部可以被允许进行跨域请求。

  • 在控制器类上使用
    可以直接在控制器类上使用 @CrossOrigin 注解,以允许整个控制器的所有方法都接受跨域请求:
import org.springframework.web.bind.annotation.CrossOrigin;  
import org.springframework.web.bind.annotation.RestController;  
  
@RestController  
@CrossOrigin(origins = "http://example.com", methods = {RequestMethod.GET, RequestMethod.POST})  
public class MyController {  
    // ... 控制器方法 ...  
}
  • 在单个方法上使用
    可以将 @CrossOrigin 注解添加到特定的控制器方法上,以仅允许该方法的跨域请求:
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 {  
  
    @GetMapping("/someMethod")  
    @CrossOrigin(origins = "http://example.com")  
    public String someMethod() {  
        // ... 方法实现 ...  
        return "Response";  
    }  
}

使用 @CrossOrigin 注解时,你可以指定多个允许的源,方法,头部等。这提供了一种灵活的方式来控制哪些跨域请求应该被允许。需要注意的是,出于安全考虑,你应该仔细考虑允许哪些源进行跨域请求,并限制允许的 HTTP 方法和头部。

最后,确保你的 Spring Boot 应用已经包含了 Spring Web 的依赖,并且你的控制器类和方法已经正确地映射到了相应的 URL 路径。这样,当来自允许源的请求到达时,Spring MVC 就会自动处理 CORS 预检请求,并在响应中包含正确的 CORS 头部。

5、手动设置响应头实现局部跨域

@RequestMapping("/test")
public String test(HttpServletResponse response) {
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", "*");
    return "test";
}

四、跨域请求在什么情况下会发送预检请求

跨域请求在以下情况下会发送预检请求:

  • 当HTTP请求方法不是GET、HEAD或POST(仅当POST请求的内容类型为application/x-www-form-urlencoded、multipart/form-data或text/plain时除外)时,会触发预检请求。这是因为浏览器在发送跨域请求时,对于非简单请求,需要通过OPTIONS方法询问服务器对跨域请求的支持情况,包括支持的请求方法、请求头和数据类型。
  • 当请求中出现自定义HTTP头部时,也会触发预检请求。这是因为浏览器需要确保目标服务器不仅支持跨域请求,还允许当前域名的访问,并允许特定的请求头部。
    预检请求是一个HTTP OPTIONS方法的请求,它包含了一些附加的头部信息,如Origin、Access-Control-Request-Method和Access-Control-Request-Headers等。服务器在收到OPTIONS请求后,会根据请求头部的信息进行验证,然后返回相应的响应头部信息。如果服务器成功响应预检请求,浏览器才会发送真正的请求,携带真实的数据。

在处理跨域请求时,后端开发人员需要对预检请求进行特殊处理,以确保跨域请求能够成功进行。

  • 33
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玉成226

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值