关于java:面试突击81什么是跨域问题如何解决

36次阅读

共计 6178 个字符,预计需要花费 16 分钟才能阅读完成。

跨域问题指的是不同站点之间,应用 ajax 无奈互相调用的问题。跨域问题实质是浏览器的一种爱护机制,它的初衷是为了保障用户的平安,避免歹意网站窃取数据。 但这个爱护机制也带来了新的问题,它的问题是给不同站点之间的失常调用,也带来的妨碍,那怎么解决这个问题呢?接下来咱们一起来看。

1. 跨域三种状况

在申请时,如果呈现了以下状况中的任意一种,那么它就是跨域申请:

  1. 协定不同,如 http 和 https;
  2. 域名不同;
  3. 端口不同。

也就是说,即便域名雷同,如果一个应用的是 http,另一个应用的是 https,那么它们也属于跨域拜访。常见的跨域问题如下图所示:

2. 跨域问题演示

接下来,咱们应用两个 Spring Boot 我的项目来演示跨域的问题,其中一个是端口号为 8080 的前端我的项目,另一个端口号为 9090 的后端接口我的项目。

2.1 前端网站

前端我的项目只须要在 resources 下放两个文件,一个用于发送 ajax 申请的 jquery.js,另一个是 html 前端页面,工程目录如下图所示:

其中前端页面 index.html 的代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title> 跨域测试页面 </title>
    <script src="js/jquery.min.js"></script>
</head>
<body>
<h1> 跨域测试 </h1>
<div>
    <input type="button" onclick="mySubmit()" value="发送跨域申请">
</div>
<script>
    function mySubmit() {
        // 发送跨域申请
        jQuery.ajax({
            url: "http://localhost:9090/test",
            type: "POST",
            data: {"name": "Java"},
            success: function (result) {alert("返回数据:" + result.data);
            }
        });
    }
</script>
</body>
</html>

2.2 后端接口

后端接口我的项目首先先在 application.properties 配置文件中,设置我的项目的端口号为 9090,如下所示:

server.port=9090

而后创立一个后端控制器,返回一个 JSON 格局的数据,实现代码如下:

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;

@RestController
public class TestController {@RequestMapping("/test")
    public HashMap<String, Object> test() {return new HashMap<String, Object>() {{put("state", 200);
            put("data", "success");
            put("msg", "");
        }};
    }
}

以上两个我的项目创立并启动胜利之后,应用前端我的项目拜访后端接口,因为端口不一样,所以也属于跨域拜访,运行后果如下图所示:

3. 解决跨域问题

在 Spring Boot 中跨域问题有很多种解决方案,比方以下 5 个:

  1. 应用 @CrossOrigin 注解实现跨域;
  2. 通过配置文件实现跨域;
  3. 通过 CorsFilter 对象实现跨域;
  4. 通过 Response 对象实现跨域;
  5. 通过实现 ResponseBodyAdvice 实现跨域。

当然如果你违心的话,还能够应用过滤器来实现跨域,但它的实现和第 5 种实现相似,所以本文就不赘述了。

3.1 通过注解跨域

应用 @CrossOrigin 注解能够轻松的实现跨域,此注解既能够润饰类,也能够润饰办法。当润饰类时,示意此类中的所有接口都能够跨域;当润饰办法时,示意此办法能够跨域,它的实现如下:

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;

@RestController
@CrossOrigin(origins = "*")
public class TestController {@RequestMapping("/test")
    public HashMap<String, Object> test() {return new HashMap<String, Object>() {{put("state", 200);
            put("data", "success");
            put("msg", "");
        }};
    }
}

以上代码的执行后果如下图所示:

从上图中能够看出,前端我的项目拜访另一个后端我的项目胜利了,也就阐明它解决了跨域问题。
优缺点剖析
此形式尽管尽管实现(跨域)比较简单,但仔细的敌人也能发现,应用此形式只能实现部分跨域,当一个我的项目中存在多个类的话,应用此形式就会比拟麻烦(须要给所有类上都增加此注解)。

3.2 通过配置文件跨域

接下来咱们 通过设置配置文件的形式就能够实现全局跨域 了,它的实现步骤如下:

  • 创立一个新配置文件;
  • 增加 @Configuration 注解,实现 WebMvcConfigurer 接口;
  • 重写 addCorsMappings 办法,设置容许跨域的代码。

具体实现代码如下:

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 CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**") // 所有接口
                .allowCredentials(true) // 是否发送 Cookie
                .allowedOriginPatterns("*") // 反对域
                .allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"}) // 反对办法
                .allowedHeaders("*")
                .exposedHeaders("*");
    }
}

3.3 通过 CorsFilter 跨域

此实现形式和上一种实现形式相似,它也能够实现全局跨域,它的具体实现代码如下:

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 MyCorsFilter {
    @Bean
    public CorsFilter corsFilter() {
        // 1. 创立 CORS 配置对象
        CorsConfiguration config = new CorsConfiguration();
        // 反对域
        config.addAllowedOriginPattern("*");
        // 是否发送 Cookie
        config.setAllowCredentials(true);
        // 反对申请形式
        config.addAllowedMethod("*");
        // 容许的原始申请头部信息
        config.addAllowedHeader("*");
        // 裸露的头部信息
        config.addExposedHeader("*");
        // 2. 增加地址映射
        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        corsConfigurationSource.registerCorsConfiguration("/**", config);
        // 3. 返回 CorsFilter 对象
        return new CorsFilter(corsConfigurationSource);
    }
}

3.4 通过 Response 跨域

此形式是解决跨域问题最原始的形式,但它能够反对任意的 Spring Boot 版本(晚期的 Spring Boot 版本也是反对的)。但此形式也是部分跨域,它利用的范畴最小,设置的是办法级别的跨域,它的具体实现代码如下:

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

import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;

@RestController
public class TestController {@RequestMapping("/test")
    public HashMap<String, Object> test(HttpServletResponse response) {
        // 设置跨域
        response.setHeader("Access-Control-Allow-Origin", "*");
        return new HashMap<String, Object>() {{put("state", 200);
            put("data", "success");
            put("msg", "");
        }};
    }
}

3.5 通过 ResponseBodyAdvice 跨域

通过重写 ResponseBodyAdvice 接口中的 beforeBodyWrite(返回之前重写)办法,咱们能够对所有的接口进行跨域设置,它的具体实现代码如下:

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    /**
     * 内容是否须要重写(通过此办法能够选择性局部控制器和办法进行重写)* 返回 true 示意重写
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {return true;}
    /**
     * 办法返回之前调用此办法
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class selectedConverterType, ServerHttpRequest request,
                                  ServerHttpResponse response) {
        // 设置跨域
        response.getHeaders().set("Access-Control-Allow-Origin", "*");
        return body;
    }
}

此实现形式也是全局跨域,它对整个我的项目中的所有接口无效。

4. 原理剖析

为什么通过以上办法设置之后,就能够实现不同我的项目之间的失常交互呢?
这个问题的答案也很简略,咱们之前在说跨域时讲到:“跨域问题实质是浏览器的行为,它的初衷是为了保障用户的拜访平安,避免歹意网站窃取数据 ”,那想要解决跨域问题就变得很简略了, 只须要通知浏览器这是一个平安的申请,“我是本人人”就行了 ,那怎么通知浏览器这是一个失常的申请呢?
只须要 在返回头中设置“Access-Control-Allow-Origin”参数即可解决跨域问题,此参数就是用来示意容许跨域拜访的原始域名的,当设置为“*”时,示意容许所有站点跨域拜访,如下图所示:

所以 以上 5 种解决跨域问题的实质都是给响应头中加了一个 Access-Control-Allow-Origin 的响应头而已。

演示我的项目源码

https://gitee.com/mydb/springboot-examples/tree/master/spring-boot-cross

总结

跨域问题的实质是浏览器为了保障用户的一种平安拦挡机制,想要解决跨域问题,只须要通知浏览器“我是本人人,不要拦我”就行。它的常见实现形式有 5 种:通过注解实现部分跨域、通过配置文件实现全局跨域、通过 CorsFilter 对象实现全局跨域、通过 Response 对象实现部分跨域,通过 ResponseBodyAdvice 实现全局跨域。

参考 & 鸣谢

blog.csdn.net/pjmike233/article/details/82461911

是非审之于己,毁誉听之于人,得失安之于数。

公众号:Java 面试真题解析

面试合集:https://gitee.com/mydb/interview

正文完
 0