- 同源,就是咱们域名、端口号、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
对于 CORS的跨域请求,主要有以下几种方式可供选择:
- 返回新的CorsFilter
- 重写 WebMvcConfigurer
- 使用注解 @CrossOrigin
- 手动设置响应头 (HttpServletResponse)
- 自定web filter 实现跨域
注意:
- 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.
- 1
错误原因: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 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 map = new HashMap<>();
User user = new User();
user.setUsername("123456");
user.setPassword("");
map.put("user",user);
return map;
}
@GetMapping("/t2")
public Map t2() {
HashMap 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);
}
}
```
> 所有解决跨域问题,不外乎就是解决浏览器拦截问题,要么前端设置请求头,要么后端设置请求头,无论谁设置请求头,浏览器只要放行即可
# 五、SpringBoot中RestTemplate
> spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。
> 在日常项目开发中,有时候我们需要调用第三方接口数据,常用的方法有传统JDK自带的URLConnection,Apache Jakarta Common下的子项目HttpClient ,Spring的RestTemplate。
> 这么一解释,就明白了RestTemplate是什么了,就是一个类似于HttpClient一样的框架,封装了一些get请求,post请求,put请求等等请求的方法,用来模拟请求,让我们可以通过Java程序想其他不同端口的服务接口访问数据。
## 1、RestTemplate API使用
### postForEntity()(有请求体)
```java
//设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
headers.add("key1","value1");
headers.add("key2","value2");
HttpEntity entity = new HttpEntity<>(new User(), headers);
String url = "http://xxx.com";
//发送post请求
ResponseEntity 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