前言
当我们创建好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的配置信息返回。这个时候我们前端可以顺利的拿到后端数据并展示。