前言
当我们创建好springboot项目写了的接口,正准备对接前端项目的时候,此时发现前端项目访问后端接口报错。
为什么会出现跨域
跨域的出现是因为浏览器的同源策略导致,浏览器出于安全的考量,防止一个网站对另一个网站的敏感信息。
同源的定义:
- 协议(protocol): 如http,https
- 域名(domain):example.com
- 端口(port):8080等端口
当出现上述的存在不同的时候,即可判定为跨域。回到我们上述出现的问题,就是我们前端react项目正以http://localhost:3000的服务启动,用axios来访问后端接口服务http://localhost:8080 ,就很明显是端口不一样所导致的问题,此时才导致的跨域。
浏览器是怎么处理跨域请求
当我们前端构建的请求发送到后端的时候,浏览器会根据同源策略检查请求是否跨域 ,同时判断请求是否是简单请求 。对于简单请求和非简单请求处理方式会有所不同。
什么是简单请求
- 请求方法 method:
get,post,head之一的 - 被浏览器自动设置的的请求头header : 如
Connection、User - Agent等 - 简单的请求头:
Accept、Accept - Language、Content - Language、Last - Modified、Content - Type(并且Content - Type的值仅限于application/x - www - form - urlencoded、multipart/form - data、text/plain)
对于简单请求的发送,浏览器会准备直接发送请求 ,但在接收到响应后,浏览器会检查响应头中的Access - Control - Allow - Origin字段值,是否允许当前源地址的访问(地址如 http://localhost:3000或者通配符*),满足条件则可以将响应结果返回个前端,不满足则控制台爆出跨域错误。
什么是非简单请求
就是不满足简单请求的就是非简单请求,如请求方法为 PUT,DELETE等方法,请求头是 Authorization等字段
对于非简单请求的发送,浏览器的处理会发送一个预请求(OPTIONS请求)到服务器,OPTIONS请求就是为了判定是否允许请求,服务端会返回具体的是否允许的响应信息。如Access - Control - Allow - Methods(允许的请求方法)、Access - Control - Allow - Headers(允许的请求头)和Access - Control - Max - Age(预检请求的有效期)等字段。只有预检查得到结果是允许了之后,浏览器才会真正发送请求。
如何解决跨域
主要就是通过配置相关的CROS跨域信息,nginx和springboot都支持配置相关的跨域信息
Access - Control - Allow - Origin(允许前端访问源的地址)Access - Control - Allow - Methods(允许前端访问的请求方法)Access - Control - Allow - Headers(允许前端访问的请求头)Access - Control - Expose - Headers(允许暴露给前端获取的请求头)
接口上的跨域配置
一种比较简便的方法,我们可以从后端的接口上进行处理,springboot有单独解决跨域处理的注解。将@CrossOrigin 注解到接口地址上/整个controller类上,配置上 origin信息
@RestController
public class TestController {
// 方法上配置
@CrossOrigin(origins = "*")
@RequestMapping("/hello")
public String hello() {
return "hello";
}
}
// 类上配置
@RestController
@CrossOrigin(origins = "*")
public class TestController {
}
这种配置属于单一的控制,书写便捷,但是缺点是对于其他接口需要一一添加,过于麻烦和繁琐。
使用拦截器
通过统一配置拦截器,对指定请求路径进行增加跨域配置的输出
@Configuration
public class WebCustomConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// 对于所有路径进行拦截,然后配置origin为*
registry.addMapping("/**")
.allowedOrigins("*");
}
}
缺点: 如果项目有过滤器的时候,请求进入的顺序会出现不同,导致跨域信息被过滤器的重写或者修改了响应头。
使用统一的CORS过滤器(推荐)
使用 Spring Boot 提供的CorsFilter来统一配置跨域。在配置类中,可以通过@Bean方法来创建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 CorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.addExposedHeader("*");
// 是否允许证书 不再默认开启
// config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
注意: 有了这个统一的CORS过滤器了,就不需要拦截器,避免混合配置。还有就是当你配置了allowCredentials 为true的时候,就必须指定allowOrigin为指定的地址,不能再只是通配符* 。
Access - Control - Allow - Credentials:这是一个跨域资源共享(CORS)相关的响应头。当它的值为true时,表示服务器允许浏览器发送带有凭据(如 Cookies、HTTP 认证信息等)的跨域请求。
请求结果
当我们配置完跨域信息后,reponse中的header会增加对于origin的配置信息返回。这个时候我们前端可以顺利的拿到后端数据并展示。