Spring Boot解决前端请求跨域问题

在前后端分离的项目中,一般都会碰到跨域问题,这里记录下解决方法。

CORS是什么

CORS全称是Cross-Origin Resource Sharing,直译过来就是跨域资源共享。要理解这个概念就需要知道域、资源和同源策略这三个概念。

  • 域:一个站点由protocal、host、port三部分组成,其中host可以是域名,也可以是ip;port如果没有指明,则是使用protocal的默认端口
  • 资源:一个URL对应的内容,可以是一张图片、一种字体、一段HTML代码、一份JSON数据等等任何形式的任何内容
  • 同源策略:为了防止XSS,浏览器、客户端应该仅请求与当前页面来自同一个域的资源,请求其他域的资源需要通过验证

了解了这三个概念,我们就能理解为什么有CORS规范了:从站点A请求站点B的资源的时候,由于浏览器的同源策略的影响,这样的跨域请求将被禁止发送;为了让跨域请求能够正常发送,我们需要一套机制在不破坏同源策略的安全性的情况下、允许跨域请求正常发送,这样的机制就是CORS。

预检请求

在CORS中定义了一种预检请求,即preflight request,当实际请求不是一个简单请求时,会发起一次预检请求。预检请求是针对实际请求的URL发起一次OPTIONS请求,并带上下面三个headers:

  • Origin:值为当前页面所在的域,用于告诉服务器当前请求的域。如果没有这个 header,服务器将不会进行CORS验证
  • Access-Control-Request-Method:值为实际请求将会使用的方法
  • Access-Control-Request-Headers:值为实际请求将会使用的header集合

如果服务器端CORS验证失败,则会返回客户端错误,即4xx的状态码。否则,将会请求成功,返回200的状态码,并带上下面这些headers

  • Access-Control-Allow-Origin:允许请求的域,多数情况下,就是预检请求中的Origin的值
  • Access-Control-Allow-Credentials:一个布尔值,表示服务器是否允许使用cookies
  • Access-Control-Expose-Headers:实际请求中可以出现在响应中的headers集合
  • Access-Control-Max-Age:预检请求返回的规则可以被缓存的最长时间,超过这个时间,需要再次发起预检请求
  • Access-Control-Allow-Methods:实际请求中可以使用到的方法集合浏览器会根据预检请求的响应,来决定是否发起实际请求

Spring Boot解决办法

方法一

全局配置,不过已过时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("*");
}
}

方法二

基于过滤器的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.springframework.context.annotation.Configuration;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter(filterName = "CorsFilter ")
@Configuration
public class CorsFilter implements Filter {
@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-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
}

方法三

注解可以放在method、class等上面,类似RequestMapping,也就是说,整个controller下面的方法可以都受控制,也可以单个方法受控制。

1
2
3
4
5
public class GoodsController {
@CrossOrigin(origins = "http://localhost:4000")
@GetMapping("goods-url")
public Response queryGoodsWithGoodsUrl(@RequestParam String goodsUrl) throws Exception {}
}

参考


----------- 本文结束啦感谢您阅读 -----------

赞赏一杯咖啡

欢迎关注我的其它发布渠道