SpringBoot解决跨域问题的六种方式

2022-06-14 11:57:46

一、同源策略

  1. 同源,就是咱们域名、端口号、ip、采用的协议都相同,那么我们就是同源的
  2. 反之就是不同源的!!!
  3. 出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
  4. 所以,用最简单的话来说,就是前端可以发请求给服务器,服务器也可以进行响应,只是因为浏览器会对请求头进行判断,所以要么前端设置请求头,要么后端设置请求头

不同源的应用场景:

  1. 本地文件,向远程服务器发送请求,可以发送,但是会出现跨域
  2. 本地服务器跑前端文件,服务器跑服务器程序,也会出现跨域问题

在这里插入图片描述

一、同源策略

  1. 同源,就是咱们域名、端口号、ip、采用的协议都相同,那么我们就是同源的
  2. 反之就是不同源的!!!
  3. 出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
  4. 所以,用最简单的话来说,就是前端可以发请求给服务器,服务器也可以进行响应,只是因为浏览器会对请求头进行判断,所以要么前端设置请求头,要么后端设置请求头

不同源的应用场景:

  1. 本地文件,向远程服务器发送请求,可以发送,但是会出现跨域
  2. 本地服务器跑前端文件,服务器跑服务器程序,也会出现跨域问题

二、跨域问题

跨域报错如下:

Access to XMLHttpRequest at 'http://localhost:8080/t1' from origin 'http://localhost:63342' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
test1.html?_ijt=aekdfma33ut4n31cgsohdrjt89:17 {readyState: 0, getResponseHeader: ƒ, getAllResponseHeaders: ƒ, setRequestHeader: ƒ, overrideMimeType: ƒ, …}
jquery-1.9.1.min.js:5          GET http://localhost:8080/t1 net::ERR_FAILED 200

三、spring boot解决跨域问题

对于 CORS的跨域请求,主要有以下几种方式可供选择:

  1. 返回新的CorsFilter
  2. 重写 WebMvcConfigurer
  3. 使用注解 @CrossOrigin
  4. 手动设置响应头 (HttpServletResponse)
  5. 自定web filter 实现跨域

1、使用CorsFilter

注意:

  • CorFilter / WebMvConfigurer / @CrossOrigin 需要 SpringMVC 4.2以上版本才支持,对应springBoot 1.3版本以上
  • 上面前两种方式属于全局 CORS 配置,后两种属于局部 CORS配置。如果使用了局部跨域是会覆盖全局跨域的规则,所以可以通过 @CrossOrigin 注解来进行细粒度更高的跨域资源控制。
  • 其实无论哪种方案,最终目的都是修改响应头,向响应头中添加浏览器所要求的数据,进而实现跨域

报错:

java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.

错误原因:2.4.0之后如果写.allowedOrigins(*)会报错

替换成.allowedOriginPatterns即可。

@Configurationpublicclass corsFilter{@BeanpublicCorsFilterCorsFilter(){CorsConfiguration corsConfiguration=newCorsConfiguration();

        corsConfiguration.addAllowedOriginPattern("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource=newUrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",corsConfiguration);returnnewCorsFilter(urlBasedCorsConfigurationSource);}}
```

##2、实现WebMvcConfigurer里面的addCorsMappings方法~~~java@Configurationpublicclass corsFilter1implementsWebMvcConfigurer{@OverridepublicvoidaddCorsMappings(CorsRegistry registry){
        registry.addMapping("/**")// 匹配所有的路径.allowCredentials(true)// 设置允许凭证.allowedHeaders("*")// 设置请求头.allowedMethods("GET","POST","PUT","DELETE")// 设置允许的方式.allowedOriginPatterns("*");}}
```





##3@CrossOrigin局部跨域通过

```Java@GetMapping("/t2")@CrossOriginpublicMapt2(){HashMap<String,Object> map=newHashMap<>();User user=newUser();
    user.setUsername("123456");
    user.setPassword("程世玉");
    map.put("user",user);return map;}
```> 也可以指定允许通过的ip

```js/**
 * 如果只是想部分接口跨域,且不想使用配置来管理的话,可以使用这种方式
 * 添加响应头解决跨域
 * @return
 */@RequestMapping(value="/user_2", method=RequestMethod.POST)@CrossOrigin(origins="http://172.16.71.27:8080", maxAge=3600)publicUsergetUser_2(@RequestParamLong id){returnnewUser(id,"Booker","admin","sdfsdkjf93hu8dvn");}
```



##4、添加响应头解决跨域

```js@RequestMapping(value="/user-1")publicUsergetUser_1(HttpServletResponse response){// 允许所有,不安全
    response.addHeader("Access-Control-Allow-Origin","*");
    response.addHeader("Access-Control-Max-Age","10");
    response.setHeader("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept");
    response.setHeader("Access-Control-Allow-Methods","GET, POST, PUT");
    response.setHeader("Access-Control-Allow-Credentials","true");returnnewUser(1L,"Booker","admin","sdfsdkjf93hu8dvn");}
```



##5、ajax跨域访问增加响应头

```js
$.ajax({
    url:"http://xxxx.xxxx.com/api/user/user-1",
    type:"post",
    dataType:"text",
    contentType:"application/json",
    data: JSON.stringify(data),
    headers:{'Content-Type':'application/json'},
    success: function(res){alert(res);}})
```

##6、手写反向代理解决跨域问题~~~java@SpringBootApplicationpublicclassSpringBoot1DayApplication{publicstaticvoidmain(String[] args){SpringApplication.run(SpringBoot1DayApplication.class, args);}@ResourceprivateRestTemplateBuilder restTemplateBuilder;@BeanpublicRestTemplaterestTemplate(){return restTemplateBuilder.build();}}
```> 代理类:通过restTemplate,模拟发请求,等于说我们只要暴露这一个接口就可以,其余接口统统可以通过这个接口进行访问!!!>> 核心方法:>>~~~java>@RequestMapping("/api/**")>@CrossOrigin>publicObjectt3(HttpServletRequest request){>String url="http://localhost:8080";>return restTemplate.getForObject( proxyAddress+ request.getRequestURI().replace("/api",""),Object.class);>}> ```> 全部代码~~~javapackagecom.example.springboot1_day.Handler;importcom.example.springboot1_day.eneity.User;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.web.bind.annotation.CrossOrigin;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;importorg.springframework.web.client.RestTemplate;importjavax.annotation.Resource;importjavax.servlet.http.HttpServletRequest;importjava.util.HashMap;importjava.util.Map;/**
 * @author 程世玉
 * @create 2022/3/23 21:32
 * @PROJECT_NAME SpringBoot-Homology
 * @Description
 */@RestControllerpublicclassUserHandler{@Value("${proxy.address}")privateString proxyAddress;@ResourceprivateRestTemplate restTemplate;@GetMapping("/t1")publicMapt1(){HashMap<String,Object> map=newHashMap<>();User user=newUser();
        user.setUsername("123456");
        user.setPassword("程世玉");
        map.put("user",user);return map;}@GetMapping("/t2")publicMapt2(){HashMap<String,Object> map=newHashMap<>();User user=newUser();
        user.setUsername("123456");
        user.setPassword("程世玉");
        map.put("user",user);return map;}@RequestMapping("/api/**")@CrossOriginpublicObjectt3(HttpServletRequest request){String url="http://localhost:8080";return restTemplate.getForObject( proxyAddress+ request.getRequestURI().replace("/api",""),Object.class);}}
```> 所有解决跨域问题,不外乎就是解决浏览器拦截问题,要么前端设置请求头,要么后端设置请求头,无论谁设置请求头,浏览器只要放行即可


# 五、SpringBootRestTemplate> spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClientRestTemplate是一种更优雅的调用RESTful服务的方式。> 在日常项目开发中,有时候我们需要调用第三方接口数据,常用的方法有传统JDK自带的URLConnectionApacheJakartaCommon下的子项目HttpClientSpringRestTemplate> 这么一解释,就明白了RestTemplate是什么了,就是一个类似于HttpClient一样的框架,封装了一些get请求,post请求,put请求等等请求的方法,用来模拟请求,让我们可以通过Java程序想其他不同端口的服务接口访问数据。

##1RestTemplate API使用

###postForEntity()(有请求体)



```java//设置请求头HttpHeaders headers=newHttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
headers.add("key1","value1");
headers.add("key2","value2");HttpEntity<User> entity=newHttpEntity<>(newUser(), headers);String url="http://xxx.com";//发送post请求ResponseEntity<R> responseEntity= restTemplate.postForEntity(url, entity,R.class);R r= responseEntity.getBody();if(HttpStatus.OK.value()!= r.getStatus()){
    log.error("发送错误:{}", r.getMsg());}
```

###postForEntity()(有请求参数)



```javaString url="http://xxx.com??p1={1}&p2={2}";ResponseEntity<Map> responseEntity= restTemplate.postForEntity(
                url,null,Map.class,"aa","bb");
```




```java

```

###postForObject()



```java//参数是Book类型,返回值也是Book类型Book book= restTemplate.postForObject("http://127.0.0.1:8080/updatebook", book,Book.class);
```

### post方法获取List数据~~~javaList<ProductTypeVO> voList=Lists.newArrayList();HttpHeaders headers=newHttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
headers.add("key1","value1");
headers.add("key2","value2");HttpEntity<String> entity=newHttpEntity<>("", headers);ProductTypeVO[] responseEntity= restTemplate.postForObject(
        url,
        entity,ProductTypeVO[].class);if(null!= responseEntity){
    voList=Arrays.asList(responseEntity);}
  • 作者:程世玉
  • 原文链接:https://blog.csdn.net/qq_46028126/article/details/123721540
    更新时间:2022-06-14 11:57:46