一、同源策略
- 同源,就是咱们域名、端口号、ip、采用的协议都相同,那么我们就是同源的
- 反之就是不同源的!!!
- 出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
- 所以,用最简单的话来说,就是前端可以发请求给服务器,服务器也可以进行响应,只是因为浏览器会对请求头进行判断,所以要么前端设置请求头,要么后端设置请求头
不同源的应用场景:
- 本地文件,向远程服务器发送请求,可以发送,但是会出现跨域
- 本地服务器跑前端文件,服务器跑服务器程序,也会出现跨域问题
一、同源策略
- 同源,就是咱们域名、端口号、ip、采用的协议都相同,那么我们就是同源的
- 反之就是不同源的!!!
- 出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
- 所以,用最简单的话来说,就是前端可以发请求给服务器,服务器也可以进行响应,只是因为浏览器会对请求头进行判断,所以要么前端设置请求头,要么后端设置请求头
不同源的应用场景:
- 本地文件,向远程服务器发送请求,可以发送,但是会出现跨域
- 本地服务器跑前端文件,服务器跑服务器程序,也会出现跨域问题
二、跨域问题
跨域报错如下:
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的跨域请求,主要有以下几种方式可供选择:
- 返回新的CorsFilter
- 重写 WebMvcConfigurer
- 使用注解 @CrossOrigin
- 手动设置响应头 (HttpServletResponse)
- 自定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);}}
```> 所有解决跨域问题,不外乎就是解决浏览器拦截问题,要么前端设置请求头,要么后端设置请求头,无论谁设置请求头,浏览器只要放行即可
# 五、SpringBoot中RestTemplate> spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。> 在日常项目开发中,有时候我们需要调用第三方接口数据,常用的方法有传统JDK自带的URLConnection,ApacheJakartaCommon下的子项目HttpClient ,Spring的RestTemplate。> 这么一解释,就明白了RestTemplate是什么了,就是一个类似于HttpClient一样的框架,封装了一些get请求,post请求,put请求等等请求的方法,用来模拟请求,让我们可以通过Java程序想其他不同端口的服务接口访问数据。
##1、RestTemplate 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);}