SpringBoot中通过CORS解决跨域问题(实战+剖析)

文章目录

      • 一、同源策略
      • 二、举例
      • 三、存在的问题
      • 四、源码地址

一、同源策略

前言

在开始实际操作之前,先来聊聊什么是同源策略?许多人都对跨域有一定的误解,认为这是前端工程师的问题,和后端没说什么关系,其实并不是的,说到跨域,就不得不说一下浏览器的同源策略。

简介

同源策略是由美国Netscape(网景)公司提出的一个著名的安全策略,它是目前市面上的浏览器最核心也是最基本的安全功能,现在所有支持JavaScript的浏览器都会使用这个策略。所谓同源是指协议、域名、端口都要相同。同源策略是基于安全层面而提出来了,其实这个策略本身并没有什么问题,但是开发者在开发中由于各种原因会存在跨域的需求。例如:一个大型项目由多个公司来完成,不同公司开发的项目域名和端口一般不同,但是各个公司之间又有数据交互的需求,这时就会存在跨域的需求。

历程

传统的跨域解决方案是JSONP,JSONP虽然能解决跨域,但是它有一个很大的弊端,就是它只支持GET请求,不支持其他类型的请求,但是我们平时常用的还有POST请求,那这种解决方案肯定不行。

而今天我们说的CORS跨域源资源共享)(CORS,Cross-origin resource sharing)是一个W3C标准,它是一份浏览器技术的规范,提供了Web服务从不同网域传来沙盒脚本的方法,以避开浏览器的同源策略,这是JSONP模式的现代版。

在Spring框架中,对于CORS也提供了解决方案,今天来说说SpringBoot中如何实现CORS。

二、举例

第一步】先创建两个SpringBoot项目,一个命名为cors1提供服务,另一个命名为cors2消费服务,第一个默认端口8080即可,第二配置端口为8081,然后在cors1上新建一个Controller,并提供一个get接口,如下:

【cors1 -> HelloController.java】

@RestController
public class HelloController {
     

    @GetMapping("/get")
    public String getData() {
     
        return "hello get cors";
    }
}

【cors2 -> application.properties】

server.port=8081

在cors2项目下的resources/static目录创建一个index.html文件,发送一个简单的get请求,如下:

【cors2 -> index.html】


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    <script src="js/jquery-3.3.1.js">script>
head>
<body>
<div id="result">div>
<input type="button" value="GET" onclick="getData()">
<script>
    function getData() {
      
        $.get('http://localhost:8080/doGet',function (msg) {
      
            $("#result").html(msg);
        })
    }
script>
body>
html>

注:记得引入jquery文件。

然后,分别启动两个项目,打开浏览器访问http://localhost:8081/index.html,按F12打开浏览器控制台,点击GET按钮,注意观察红色提示,如下:

Access to XMLHttpRequest at 'http://localhost:8080/doGet' from origin 'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

可以看到,由于同源策略,所以该请求无法发送成功。

使用CORS可以在前端代码不做任何修改的前提下实现跨域,接下来我们应该在cors1项目中如何进行配置?在GET请求的方法上,添加@CrossOrigin注解,配置某一个方法接收某一个域的请求,如下:

@RestController
public class HelloController {
     

    @GetMapping("/doGet")
    @CrossOrigin(origins = "http://localhost:8081")
    public String getData() {
     
        return "hello get cors";
    }
}

添加了这个注解的接口,表示接受来自http://localhost:8081地址的请求,重启cors1项目,打开浏览器访问,这次不会出现前面的错误了,cors2也能拿到数据了,如下:
SpringBoot中通过CORS解决跨域问题(实战+剖析)_第1张图片
这个表示服务端允许接收来自http://localhost:8081的请求,拿到该信息后,浏览器就不会再去限制本次请求的跨域了。

问题一

在cors1项目中,如果每个请求对应一个接口,那每个方法上都要添加@CrossOrigin注解,那太麻烦了吧???

方法一

但是,在SpringBoot中,可以全局配置一次性来解决这个问题,那就是把这个@CrossOrigin注解放到这个类上即可,如下:

@RestController
@CrossOrigin(origins = "http://localhost:8081")
public class HelloController {
     

    @GetMapping("/doGet")
    public String getData() {
     
        return "hello get cors";
    }
}

为啥这个注解可以加到类上呢?你为什么不早说?

@CrossOrigin源码解读

下面,我们来看看源码,我们进入这个@CrossOrigin注解中看看,如下:
SpringBoot中通过CORS解决跨域问题(实战+剖析)_第2张图片
看到上面箭头没有,这就是为啥,说明这个注解在类和方法上都能生效。

问题二

在cors1项目中,如果每个请求都对应一个类中的某个方法呢?那我是不是每个类都得添加@CrossOrigin注解,也很麻烦啊???

方法二

其实,可以用一个配置类来解决这个问题,这个全局配置只需要在配置类中重写addCorsMappings方法即可,如下:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
     
    @Override
    public void addCorsMappings(CorsRegistry registry) {
     
        registry.addMapping("/**").allowedOrigins("http://localhost:8081")
                .allowedHeaders("*")
                .allowedMethods("*")
                .maxAge(30*1000);
    }
}

/**表示本应用的的所有方法都会去处理跨域请求,allowedOrigins表示允许处理跨域请求的协议、域名、端口,allowedHeaders表示允许的请求头,allowedMethods表示允许的请求类型(如:GET、POST、PUT等),maxAge表示有效时间(单位:ms,如:发送PUT请求时,第一次发送PUT请求会发送两次请求,先发送一次探测请求,这时maxAge方法就有作用了,如果在有效时间内完成,会再发一次正式的PUT请求)。

小结

综上所述,经过这么一次全局性的配置,就不需要在每个类,甚至每个方法上单独进行配置了。

三、存在的问题

了解整个CORS工作过程后,我们通过Ajax发送跨域请求,虽然使得用户体验提高了,但同时也伴随着其他的风险,比较常见的就是:CSRF(Cross-site request forgery)跨站请求伪造。跨站请求伪造也被称为one-click attack 或者 session riding,通常缩写为CSRF或者XSRF,是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法,举例:

假如一家银行用以运行转账操作的URL地址如下:https://icbc.com/accounts?name=xxx,那么,一个恶意攻击者可以在另一个网站上放置如下代码:,如果用户无意中访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会遭受损失。

基于上述情况,浏览器在实际操作中,会对请求进行分类,分为简单请求,预先请求,带凭证的请求等,预先请求会首先发送一个options探测请求(PUT请求就是如此),和浏览器进行协商是否接受请求。默认情况下跨域请求是不需要凭证的,但是服务端可以配置要求客户端提供凭证,这样就可以有效避免CSRF攻击。

四、源码地址

cors1源码地址:cors1
cors2源码地址:cors2

你可能感兴趣的:(SpringBoot,#,SpringBoot整合Web,java,spring,web,spring,boot)