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

2年前 (2022) 程序员胖胖胖虎阿
200 0 0

一、同源策略

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

不同源的应用场景:

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

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

一、同源策略

  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即可。

@Configuration
public class corsFilter {
    @Bean
    public CorsFilter CorsFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();

        corsConfiguration.addAllowedOriginPattern("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);


        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",corsConfiguration);

        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
}
```

## 2、实现WebMvcConfigurer里面的addCorsMappings方法

~~~java
@Configuration
public class corsFilter1 implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")  // 匹配所有的路径
                .allowCredentials(true) // 设置允许凭证
                .allowedHeaders("*")   // 设置请求头
                .allowedMethods("GET","POST","PUT","DELETE") // 设置允许的方式
                .allowedOriginPatterns("*");
    }
}
```





## 3@CrossOrigin局部跨域通过

```Java
@GetMapping("/t2")
@CrossOrigin
public Map t2() {
    HashMap<String, Object> map = new HashMap<>();
    User user = new User();
    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)
public User getUser_2(@RequestParam Long id) {
    return new User(id, "Booker", "admin", "sdfsdkjf93hu8dvn");
}
```



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

```js
@RequestMapping(value = "/user-1")
public User getUser_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");

    
    return new User(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
@SpringBootApplication
public class SpringBoot1DayApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBoot1DayApplication.class, args);
    }


    @Resource
    private RestTemplateBuilder restTemplateBuilder;

    @Bean
    public RestTemplate restTemplate() {
        return restTemplateBuilder.build();
    }



}
```





> 代理类:通过restTemplate,模拟发请求,等于说我们只要暴露这一个接口就可以,其余接口统统可以通过这个接口进行访问!!!
>
> 核心方法:
>
>~~~java
> @RequestMapping("/api/**")
> @CrossOrigin
> public Object t3(HttpServletRequest request) {
>  String url = "http://localhost:8080";
>  return restTemplate.getForObject( proxyAddress+ request.getRequestURI().replace("/api", ""), Object.class);
> }
> ```

> 全部代码

~~~java
package com.example.springboot1_day.Handler;

import com.example.springboot1_day.eneity.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 程世玉
 * @create 2022/3/23 21:32
 * @PROJECT_NAME SpringBoot-Homology
 * @Description
 */
@RestController
public class UserHandler {

    @Value("${proxy.address}")
    private String proxyAddress;

    @Resource
    private RestTemplate restTemplate;


    @GetMapping("/t1")
    public Map t1() {
        HashMap<String, Object> map = new HashMap<>();
        User user = new User();
        user.setUsername("123456");
        user.setPassword("程世玉");
        map.put("user",user);

        return map;
    }


    @GetMapping("/t2")
    public Map t2() {
        HashMap<String, Object> map = new HashMap<>();
        User user = new User();
        user.setUsername("123456");
        user.setPassword("程世玉");
        map.put("user",user);

        return map;
    }


    @RequestMapping("/api/**")
    @CrossOrigin
    public Object t3(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自带的URLConnectionApache Jakarta Common下的子项目HttpClientSpringRestTemplate> 这么一解释,就明白了RestTemplate是什么了,就是一个类似于HttpClient一样的框架,封装了一些get请求,post请求,put请求等等请求的方法,用来模拟请求,让我们可以通过Java程序想其他不同端口的服务接口访问数据。

## 1RestTemplate API使用

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



```java
//设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
headers.add("key1","value1");
headers.add("key2","value2");

HttpEntity<User> entity = new HttpEntity<>(new User(), 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()(有请求参数)



```java
String 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数据

~~~java
List<ProductTypeVO> voList = Lists.newArrayList();

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
headers.add("key1","value1");
headers.add("key2","value2");

HttpEntity<String> entity = new HttpEntity<>("", headers);
ProductTypeVO[] responseEntity = restTemplate.postForObject(
        url,
        entity,
        ProductTypeVO[].class);

if (null != responseEntity) {
    voList = Arrays.asList(responseEntity);
}

版权声明:程序员胖胖胖虎阿 发表于 2022年9月15日 下午6:08。
转载请注明:SpringBoot解决跨域问题的六种方式 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...