# SpringBoot3教程 - 16 跨域配置

# 16.1 概念解释

# 16.1 什么是动静分离

动静分离是指在Web服务器架构中,将静态页面和动态内容接口分为不同系统的架构设计方法。

目前比较常用的方式是:将静态资源(如HTML、CSS、JavaScript、图像文件等)使用 Nginx 来部署,页面上展示的动态数据内容使用后端的 SpringBoot 接口来提供。

使用动静分离有如下优势:

  • 性能优化:静态内容通常可以通过 CDN(内容分发网络)进行缓存和分发,减轻服务器负担,加快用户访问速度。
  • 资源利用:动态内容和静态内容由不同的服务器处理,可以根据各自的需求进行优化,充分利用服务器资源。
  • 安全性:将动态和静态内容分离,可以减少服务器暴露在外的攻击面,提高整体安全性。
  • 维护性:静态资源可以独立更新,不影响动态内容的生成和处理,提高网站的可维护性。

# 16.2 什么是跨域

跨域(Cross-Origin)是指浏览器的同源策略(Same-Origin Policy),同源策略是浏览器的一种安全策略,浏览器会限制了从一个域(域名、协议和端口相同)加载的文档或脚本去访问另一个不同域的资源。这种限制是为了防止一个网站恶意读取另一个网站的敏感信息。


例如:A网站保存了一些敏感的Cookie信息,如果没有同源限制,B网站就可以读取A网站的Cookie内容了,敏感数据就会泄露,并且冒充用户访问A网站。所以在一个网站中,是无法通过Javascript访问另一个不同域(协议、域名、端口不同)的网站的接口的。

以下 URL 都被认为是不同的源:

  • http://doubibiji.comhttp://www.doubibiji.com(子域名不同)
  • http://www.doubibiji.com:80http://www.doubibiji.com:8080(端口不同)
  • http://www.doubibiji.comhttps://www.doubibiji.com(协议不同,http和https)

因为动静分离会将网站的静态资源与后台应用分开部署,它们可能具有不同的域名、子域名或端口。在这种情况下,如果前端代码尝试从不同的域或子域请求后端接口,就可能会遇到跨域问题。

例如访问前端的URL是 http://www.doubibiji.com/user ,在前端页面发起请求后端的接口是 http://www.doubibiji.com:8080/user/userList ,那么就会遇到跨域问题。浏览器会报如下错误:

Access to XMLHttpRequest at 'http://www.doubibiji.com:8080/user/userList' from origin 'http://www.doubibiji.com/user' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
1

# 16.3 跨域问题的解决方法

# 1 JSONP

JSONP(JSON with Padding)是一种通过 <script> 标签加载 JSON 数据的方式,但它只支持 GET 请求。JavaScript跨域请求数据是不可以的,但是使用 <script> 标签跨域请求其他站点的JavaScript脚本是可以的。可以把数据封装到JavaScript脚本中,作为脚本中函数的参数,然后将脚本返回给客户端,客户端获取脚本后,立即执行脚本,就可以获取到函数的参数,也就是服务器传递的数据了,从而解决了跨域问题。

JSONP是一种比较老的方式,目前已经被 CORS 大部分取代。

# 2 CORS

这是一种在服务器端通过设置适当的 CORS 头来允许跨域请求。也就是服务器告诉浏览器,我允许被跨域来访问,不要拦截了。

# 3 使用反向代理

通过配置反向代理,前端所有的请求都会发送到同一个域名,也就是 Nginx 接收所有的请求,包括前端静态页面和后端动态数据接口的请求,然后 Nginx 反向代理服务器再根据请求路径将后端请求转发到对应的后端服务器,这就避免了浏览器的跨域限制。

# 16.2 SpringBoot跨域配置

下面来介绍 SpringBoot 服务支持跨域的配置,SpringBoot 是服务器的配置方式,所以使用的CORS。

下面介绍两种常用的配置方式。

# 1 实现WebMvcConfigurer

创建配置类,实现 WebMvcConfigurer 接口,并实现 addCorsMappings 方法。

package com.doubibiji.hellospringboot.config;

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("*")  // 所有地址都可以访问
                .allowCredentials(true)  // 是否允许请求携带凭证(如 Cookies)
                .allowedMethods("*")  // 允许所有的HTTP请求方法(如"GET","POST","PUT","DELETE","OPTIONS"等)
                .allowedHeaders("*")  // 允许所有的请求头
                .maxAge(3600);  // 跨域请求的缓存时间

        // 或者
        // 可以限制允许跨域的接口、域名、请求方法等
        registry.addMapping("/api/**")  // 只允许 /api/** 路径下的接口
                .allowedOrigins("https://www.doubibiji.com", "https://www.doubibiji.com:8080")  // 只允许指定的域名访问
                .allowCredentials(true)  // 是否允许请求带有验证信息
                .allowedMethods("GET", "POST")  // 只允许 GET 和 POST 请求方法
                .allowedHeaders("*")  // 允许所有的请求头
                .maxAge(3600);  // 跨域请求的缓存时间
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

addMapping() 方法可以调用多次,进行多次设置,从而针对不用的域名采用不同的跨域限制。

# 2 使用@CrossOrigin注解

在 controller 的类或方法上添加 @CrossOrigin 注解,如果添加在类上,表示 controller 中所有的接口都允许跨域请求。

package com.doubibiji.hellospringboot.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

// 允许所有的域名访问
@CrossOrigin("*")  
// 允许一个域名来访问
//@CrossOrigin("http://www.doubibiji.com")  
// 允许多个域名来访问
//@CrossOrigin({"http://www.doubibiji.com", "http://www.doubibiji.com:8080"})  
@RestController
public class HelloWorldController {
    
    @GetMapping("/hello")
    public String hello() {
        return "Hello World!";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19