zoukankan      html  css  js  c++  java
  • Spring Boot 通过CORS实现跨域

    同源策略

    很多人对跨域有一种误解,以为这是前端的事,和后端没关系,其实不是这样的,说到跨域,就不得不说说浏览器的同源策略。

    同源策略是由 Netscape 提出的一个著名的安全策略,它是浏览器最核心也最基本的安全功能,现在所有支持 JavaScript 的浏览器都会使用这个策略。所谓同源是指协议、域名以及端口要相同。同源策略是基于安全方面的考虑提出来的,这个策略本身没问题,但是我们在实际开发中,由于各种原因又经常有跨域的需求,传统的跨域方案是 JSONP,JSONP 虽然能解决跨域但是有一个很大的局限性,那就是只支持 GET 请求,不支持其他类型的请求,而今天我们说的 CORS(跨域源资源共享)(CORS,Cross-origin resource sharing)是一个 W3C 标准,它是一份浏览器技术的规范提供了 Web 服务从不同网域传来沙盒脚本的方法,以避开浏览器的同源策略,这是 JSONP 模式的现代版。

    实践

    首先,我们新建两个工程:

    新建工程一:cors1 project

    勾选Web模块 因为我们等下需要通过web接口进行测试 点击Finish完成构建

    在cors1 中 我们新建一个HelloController,写上一个测试接口:

    @RestController
    public class HelloController {
    
        @GetMapping("/hello")
        public String hello(){
            return "hello cors1";
        }
    }
    

    新建工程二:cors2 project


    对应的勾选Web模块进行构建。

    在cors2的static目录下,建立Index.html,并编写一个GET请求按钮,发起Ajax请求(前提:static目录下有jquery.js)
    请求cors1 工程的 localhost:8080/hello接口

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="jquery3.3.1.js"></script>
    </head>
    <body>
    <div id="app"></div>
    <input type="button" value="GET" onclick="getData()">
    <input type="button" value="PUT" onclick="putData()">
    <script>
        function getData() {
            $.get("http://localhost:8080/hello",function (msg) {
                $("#app").html(msg);
            })    
        }
    </script>
    </body>
    </html>
    

    并设定cors2 工程的项目端口:

    server.port = 8081
    

    分别启动cors1 和 cors2

    访问 localhost:8081/index.html 点击按钮,发送请求:

    访问报错,目前不支持跨域,验证了上面说的 同源策略

    解决方案一:

    1.在Controller类 或 接口方法上,使用 注解 @CrossOrigin 指定允许哪个域服务器访问

    @RestController
    public class HelloController {
    
        @GetMapping("/hello")
        @CrossOrigin(origins = "http://localhost:8081")
        public String hello(){
            return "hello cors1";
        }
    }
    

    重启 Cors1 项目,点击发送请求:

    访问成功,浏览器控制台也没有报错

    注意:
    这种方式有一个弊端,就是我们需要在对外开放的每个接口或者类上都要写一遍,大大增加了开发的重复性和繁琐性

    解决方案二

    Spring Boot 中,可以通过全局配置一次性解决这个问题,全局配置只需要在 SpringMVC 的配置类中重写 **addCorsMappings **方法即可

    package org.taoguoguo;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    /**
     * @author taoguoguo
     * @description WebMvcConfig
     * @website https://www.cnblogs.com/doondo
     * @create 2020-09-01 21:31
     */
    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    
        /**
         * 跨域全局配置
         * @param registry
         */
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            //addMapping 允许哪些接口跨域
            //allowedOrigins 允许哪个域服务器访问
            //allowedHeaders 允许通过的请求头
            //maxAge 服务器在发送一些请求(例如put)会先发一个探测请求,避免每次都需要发送探测请求 可以设置有效期
            registry.addMapping("/**")
                    .allowedOrigins("http://localhost:8081")
                    .allowedHeaders("*")
                    .allowedMethods("*")
                    .maxAge(30*1000);
        }
    }
    
    

    /** 表示本应用的所有方法都会去处理跨域请求,allowedMethods 表示允许通过的请求数,allowedHeaders 则表示允许的请求头。经过这样的配置之后,就不必在每个方法上单独配置跨域了

    现在再去页面访问,也是没有问题的

    探测请求

    我们在跨域设置中

    maxAge 服务器在发送一些请求(例如put)会先发一个探测请求,避免每次都需要发送探测请求 可以设置有效期
    

    先举一个例子:
    我们在Cors1中添加一个put请求

    @RestController
    public class HelloController {
    
        @GetMapping("/hello")
        @CrossOrigin(origins = "http://localhost:8081")
        public String hello(){
            return "hello cors1";
        }
    
        @PutMapping("/doput")
        public String doput(){
            return "doput";
        }
    }
    

    在cors2中增加put请求发送按钮

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="jquery3.3.1.js"></script>
    </head>
    <body>
    <div id="app"></div>
    <input type="button" value="GET" onclick="getData()">
    <input type="button" value="PUT" onclick="putData()">
    <script>
        function getData() {
            $.get("http://localhost:8080/hello",function (msg) {
                $("#app").html(msg);
            })
        }
        
        function putData() {
            $.ajax({
                type:'put',
                url: 'http://localhost:8080/doput',
                success:function (msg) {
                    $("#app").html(msg);
                }
            })
            
        }
    </script>
    </body>
    </html>
    

    启动cors1 cors2 发送put请求:

    我们看到请求一次 浏览器会发送两次请求,其中Request Methods 为 options 的即为探测请求
    因为我们设置了探测请求的有效期,因此当我们再次发送时,浏览器只会发送一次请求

    了解了整个 CORS 的工作过程之后,我们通过 Ajax 发送跨域请求,虽然用户体验提高了,但是也有潜在的威胁存在,常见的就是 CSRF(Cross-site request forgery)跨站请求伪造。跨站请求伪造也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF,是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法

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

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

  • 相关阅读:
    vue自定义指令
    ZOJ Problem Set–2104 Let the Balloon Rise
    ZOJ Problem Set 3202 Secondprice Auction
    ZOJ Problem Set–1879 Jolly Jumpers
    ZOJ Problem Set–2405 Specialized FourDigit Numbers
    ZOJ Problem Set–1874 Primary Arithmetic
    ZOJ Problem Set–1970 All in All
    ZOJ Problem Set–1828 Fibonacci Numbers
    要怎么样调整状态呢
    ZOJ Problem Set–1951 Goldbach's Conjecture
  • 原文地址:https://www.cnblogs.com/doondo/p/13599248.html
Copyright © 2011-2022 走看看