SpringBoot跨域问题

什么是跨域

首先,我们需要了解一下一个URL是怎么组成的:

// 协议 + 域名(子域名 + 主域名) + 端口号 + 资源地址
http://www.baidu.com:8080/

只要协议,子域名,主域名,端口号这四项组成部分中有一项不同,就可以认为是不同的域,不同的域之间互相访问资源,就被称之为跨域。

跨域是什么?

 跨域是指不同域名之间的相互访问,这是由浏览器的同源策略决定的,是浏览器对JavaScript施加的安全措施,防止恶意文件破坏。

同源策略:同源策略是一种约定,它是浏览器最核心的也是最基本的安全策略,如果缺少了同源策略,则浏览器的正常功能可能会受到影响。所谓同源就是说协议 ,域名端口号完全一致,有一个不一致就会造成跨域问题。

跨域原理

  • 跨域请求能正常发出去,服务端能接受到请求并正常返回结果,只是结果被拦截了。

  • 跨域只存在于浏览器,不存在于其他平台,比如安卓/java/ios等平台。

  • 之所以会发生跨域是因为受到了同源策略的限制,同源策略要求源相同才能进行正常通信,即协议,域名,端口号都完全一致。

URL :统一资源定位符,它是www的统一资源定位标志,也就是我们说的网络地址。它的般格式为:协议类型://服务器地址:端口号/路径。这也就是我们说的跨域中的域。

随着前后端分离开发的越来越普及,会经常遇到跨域的问题,当我们在浏览器中看到这样的错误时,就需要意识到遇到了跨域:

解决跨域的三种方案

首先,我们使用vue-cli来快速构建一个前端项目,然后使用axios来向后台发送ajax请求。最后在控制台中打印出返回信息。这里就不再多做赘述,后面我会单独写一篇文章来讲一下如何使用vue-cli快速创建一个vue项目。

这里不再讲解使用jsonp的方式来解决跨域,因为jsonp方式只能通过get请求方式来传递参数,而且有一些不便之处。

下面的几种方式都是通过跨域访问技术CORS(Cross-Origin Resource Sharing)来解决的。具体的实现原理我们不做深入的探究,这节课的目的是解决跨域问题~

在Spring Boot 中给我们提供了一个注解@CrossOrigin来实现跨域,这个注解可以实现方法级别的细粒度的跨域控制。我们可以在类或者方添加该注解,如果在类上添加该注解,该类下的所有接口都可以通过跨域访问,如果在方法上添加注解,那么仅仅只限于加注解的方法可以访问。

1
CrossOrigin注解

//@CrossOrigin  表示所有的URL均可访问此资源
@CrossOrigin(origins = "http://127.0.0.1:8093")//表示只允许这一个url可以跨域访问这个controller
@RestController
@RequestMapping("/testCorss")
public class CorssOriginController {
    //可以对方法运用该注解
    //@CrossOrigin(origins = "http://127.0.0.1:8093")
    @GetMapping("/getString")
    public String getString(){
        return "跨域成功!";
    }
}

 代码说明:@CrossOrigin这个注解用起来很方便,这个可以用在方法上,也可以用在类上。如果你不设置他的value属性,或者是origins属性,就默认是可以允许所有的URL/域访问。


value属性可以设置多个URL。

origins属性也可以设置多个URL。

maxAge属性指定了准备响应前的缓存持续的最大时间。就是探测请求的有效期。

allowCredentials属性表示用户是否可以发送、处理 cookie。默认为false

allowedHeaders 属性表示允许的请求头部有哪些。

methods 属性表示允许请求的方法,默认get,post,head。

现在再去测试一下:

Bingo,成功!

2
实现WebMvcConfigurer接口

这里可以通过实现WebMvcConfigurer接口中的addCorsMappings()方法来实现跨域。

public class MyWebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/testCross/**")
                .allowedHeaders("*")
                .allowedMethods("*")
                .allowCredentials(true)
                .allowedOrigins("http://localhost:8093")
                .maxAge(2000);
    }
}
下面我们将刚刚加上的注解给注释掉,看看能不能访问到这个接口:

这个没什么好说的,就重写addCorsMappings方法就行,配置好参数,参数和上面的注解的参数类似。这个配置属于全局配置,配置好了全部的接口都遵循此规则。上面的注解方式只对类或者方法生效。addMaping是设置对那种格式的URL生效,也就是跟在URL后面的路径。~

3
过滤器Filter配置
@Configuration
public class Filter {
    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://localhost:8093");//*表示允许所有
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config); // CORS 配置对所有接口都有效
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }

}

利用过滤器配置实现跨域,还有另外一种方法

@Configuration
public class CorssFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse res = (HttpServletResponse) response;
        res.addHeader("Access-Control-Allow-Credentials", "true");
        res.addHeader("Access-Control-Allow-Origin", "http://localhost:8093");//允许的地址,*表示所有
        res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); //允许访问的方式
        res.addHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN");
        chain.doFilter(request, response);
    }
    @Override
    public void destroy() {
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
}
不出意外的话,应该也可以在控制台获取到返回信息。

Nginx配置解决跨域问题(扩展)

如果我们在项目中使用了Nginx,可以在Nginx中添加以下的配置来解决跨域(原理其实和Filter类似,只不过把活儿丢给了运维)

location / {
   add_header Access-Control-Allow-Origin *;
   add_header Access-Control-Allow-Headers X-Requested-With;
   add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;

   if ($request_method = 'OPTIONS') {
     return 204;
   }
}

 跨域测试

 我们在新建一个SpringBoot web项目,然后在resources 文件夹里面的static 新建一个index.html 代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>
<body>
<div id="test"></div>
<input type="button" value="测试数据" onclick="getString()"/>
</body>
<script>

    function getString() {
        $.ajax({
            url:'http://localhost:8092/testCorss/getString',
            type:'get',
            data:{},
            success:function (msg) {
                $("#test").html(msg);
            }
        })
    }
</script>
</html>

我们模拟一个ajax请求,来请求我们的8092端口,运行新的项目结果


点击测试,发起请求


成功从一个项目里面拿到了另外一个项目的数据,就说明我们的跨域是成功了的。

总结:
 本文讲述了跨域的产生原因,原理,以及同源策略的概念,然后介绍了在SpringBoot解决跨域的三种方式,分别是利用CrossOrgin注解,全局配置Mvc,然后就是利用过滤器配置,过滤器有两种方式,有一种是利用servlet的过滤器实现。


全部评论