SpringBoot教程(七) | SpringBoot解决跨域问题

转载一缕82年的清风大佬的文章(仅仅供个人学习用):
SpringBoot教程(七) | SpringBoot解决跨域问题

上篇文章我们介绍了SpringBoot的拦截器的写法,其中有一个比较重要的步骤,就是把我们写好的拦截器注册到Spring的一个配置类中,这个类是实现了WebMvcConfigurer 接口,这个类很重要,因为这个类中除了可以注册拦截以外,还可以配置很多内容。今天我们来讲解一下SpringBoot如何解决跨域问题。 先来解释一下什么是跨域问题。

7.1 什么是跨域?

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

7.2 为什么会出现跨域

出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。
所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)

但是以前的程序为什么没有过跨域问题的困扰呢。那是因为以前的程序都不是前后端分离的。比如之前的jsp, freemarker实现的前端,他们和后端的代码都是放到一起的,所以他们一起部署,具有相同的域名,协议和端口号,自然不存在跨域的问题。

但是现在的程序都是前后端分离的程序,前后端分离的程序有什么特点,就是前后端都是单独部署的,前端服务和后端服务都会监听属于自己的端口号,所有很容易产生跨域的问题。当前端端不同源的时候,而前端的服务又需要去访问和他不同源的后端的接口,自然就产生了跨域的问题,所以在前后端分离的项目中,跨域问题是我们永远都绕不开的。

7.3 如何解决?

既然已经阐述了跨域问题,那我们应该如何解决呢?其实解决的方案还是不少的,我们列举几种常见的方法。

1. JSONP:
JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求。

核心思想:网页通过添加一个

这种方式是前端的实现,其实和后端关系不大。并且用的也不多。

2. Nginx 反向代理
使用 nginx 反向代理实现跨域,是最简单的跨域方式。只需要修改 nginx 的配置即可解决跨域问题,支持所有浏览器,支持 session,不需要修改任何代码,并且不会影响服务器性能。

我们只需要配置nginx,在一个服务器上配置多个前缀来转发http/https请求到多个真实的服务器即可。这样,这个服务器上所有url都是相同的域 名、协议和端口。因此,对于浏览器来说,这些url都是同源的,没有跨域限制。而实际上,这些url实际上由物理服务器提供服务。这些服务器内的 javascript可以跨域调用所有这些服务器上的url。

3. webpack本地代理
这个也是前端的一种实现,通过webpack技术,就可以将服务端进行反向代理,相当于前端模拟了nginx的功能,将后端的接口反代成一个同源的地址

4. CORS
CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。

1、普通跨域请求:只需服务器端设置Access-Control-Allow-Origin

2、带cookie跨域请求:前后端都需要进行设置

服务器端对于CORS的支持,主要是通过设置Access-Control-Allow-Origin来进行的。
如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。

我们重点讲解下springBoot如何通过设置CORS 来解决跨域问题。

7.4 解决方案(针对后端)

方案一(使用CorsFilter注入Bean):

这是一种全局配置方式,适用于项目中大部分接口都需要跨域的场景。

示例一(允许来自任何域的跨域请求):
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() {  
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();  
        CorsConfiguration config = new CorsConfiguration();  
        // 允许所有域的跨域请求  
        config.addAllowedOrigin("*");  
        // 允许请求携带cookie  
        config.setAllowCredentials(true);  
        // 允许所有请求头  
        config.addAllowedHeader("*");  
        // 允许所有请求方法  
        config.addAllowedMethod("*");  
        // 添加到source中  
        source.registerCorsConfiguration("/**", config);  
        // 返回CorsFilter  
        return new CorsFilter(source);  
    }  
}
示例二(根据请求的 URL 路径来指定不同的 CORS 配置):
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() {  
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();  
        
        CorsConfiguration config1 = new CorsConfiguration();  
        //允许向该的服务器发送的某些请求
        config1.addAllowedOrigin("https://example.com");  
        //请求头  
        config1.addAllowedHeader("*");  
        //请求方法    
        config1.addAllowedMethod("GET", "POST");  
        //添加到source中  
        source.registerCorsConfiguration("/api/**", config1);  
  
       CorsConfiguration config2 = new CorsConfiguration();  
       //允许向该的服务器发送的某些请求
       config2.addAllowedOrigin("*");  
       //请求头  
       config2.addAllowedHeader("*");  
       //请求方法  
       config2.addAllowedMethod("*");  
       //添加到source中  
       source.registerCorsConfiguration("/web/**", config2);  
  
      CorsFilter corsFilter = new CorsFilter(source);
    }  
}

以上这个例子:

我们为 /api/** 路径注册了一个 CORS 配置,它只允许来自 https://example.com 的请求,并且只允许 GETPOST 方法。

而对于 /web/** 路径,我们则允许来自任何源的请求,并且允许任何请求头和请求方法。

最后,我们创建了一个 CorsFilter 实例,并将这个配置源传递给它。

方案二(实现WebMvcConfigurer的addCorsMappings方法)

这同样是一种全局配置方式,但更加符合Spring MVC的风格。

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("https://example.com") // 允许的源  
                .allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的方法  
                .allowedHeaders("*") // 允许的头  
                .allowCredentials(true); // 是否允许发送Cookie  
    }  
}

方案三(Filter过滤器实现)

除了这种方法,也可以通过Filter来实现。

主要注解@WebFilter

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
 
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
 
 
/**
 * 自定义拦截器
 *
 * @author lvhuimin
 * @version 2019/12/21
 */
@Order(1)
@Component
@WebFilter(urlPatterns = "/*", filterName = "cooCorsFilter")
public class CooCorsFilter implements Filter {
 
    String ORIGIN = "Origin";
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
 
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        String origin = httpRequest.getHeader(ORIGIN);
 
        httpResponse.setCharacterEncoding("UTF-8");
        httpResponse.setContentType("application/json; charset=utf-8");
        httpResponse.setHeader("Access-Control-Allow-Origin", origin);
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setHeader("Access-Control-Allow-Methods", "*");
        // 如果允许所有header,就用*
        httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type,Authorization,token,userId,sysCode,requestSourceType");
        httpResponse.setHeader("Access-Control-Expose-Headers", "*");
        filterChain.doFilter(httpRequest, httpResponse);
    }
 
    @Override
    public void destroy() {
 
    }
 
}

这个就是写了一个Filter然后在过滤器中,来加上相应的设置。

方案四(使用@CrossOrigin注解)

这是一种局部配置方式,适用于需要细粒度控制跨域的场景。
单独使用@CrossOrigin(不配置origins )表示允许所有源请求访问

1.在控制器类上使用(影响该控制器下的所有方法):

import org.springframework.web.bind.annotation.CrossOrigin;  
import org.springframework.web.bind.annotation.RestController;  
  
@RestController  
@CrossOrigin(origins = "https://example.com") 
// @CrossOrigin  直接使用这个表示允许所有
public class MyController {  
    // ...  
}

2.在控制器方法上使用(只影响该方法):

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 = "https://example.com")  
    // @CrossOrigin  直接使用这个表示允许所有
    @GetMapping("/greeting")  
    public String greeting() {  
        return "Hello, World!";  
    }  
}

好了跨域的问题我们就讲解到这里。但其实还是稍微有点问题,问题就是当我们的SpringBoot项目集成了Swagger的时候,上面的过滤器会和swagger的配置产生冲突,这个等到我们讲到swagger的时候再说。

另: 配套项目代码已托管中gitCode: gitcode.net/lsqingfeng/…

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值