简单请求
满足发下条件会被视为简单请求:
1、使用下列请求方法之一:
- GET
- HEAD
- POST
2、请求头信息不超出以下几种:
- Accept
- Accept-Language
- Content-Language
- Content-Type 仅限于以下三者
text/plain
multipart/form-data
application/x-www-form-urlencoded
简单请求会在请求头添加 Origin 字段,服务器根据这个值来决定是否同意这次请求。如果同请求,服务器响应会额外添加几个字段:
- Access-Control-Allow-Origin(必须)
指定了允许访问该资源的域,其值要么是请求时 Origin 的值,要么是 * 表示接受任意域名的请求;
当指定的值不是 * 时,响应首部中的 Vary 字段的值必须包含 Origin;
- Access-Control-Allow-Credentials(可选)
当浏览器的credentials设置为true时,是否允许浏览器读取response的内容;
返回true(唯一有效值,区分大小写)则可以,其他值均不可以。
此字段返回true时,Access-Control-Allow-Origin 必须为请求 Origin 的值,不能是 *;
此字段在预检请求的响应中时,它指定了实际的请求是否可以使用credentials;
简单请求不会被预检,当浏览器的credentials设置为true时,其响应中如果不包含该字段,浏览器将无法读取response的内容。
- Access-Control-Expose-Headers(可选):
白名单,列出了服务器允许浏览器访问的头;
默认情况下,XMLHttpRequest.getResponseHeader() 方法只能拿到六种简单响应首部,如果想要能拿到其它字段,就需要在Access-Control-Expose-Headers指定。
六种简单响应首部是:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
非简单请求
非简单请求是那种对服务器有特殊要求的请求
比如请求方法是 PUT 或 DELETE;
比如包含了限定以外的请求头,如 Authorization 或 X-Custom-Header;
比如 Content-Type 字段值是 application/json;
对于非简单请求,浏览器会先发起一个预检请求,得到肯定答复后,才会发起正式请求。
预检请求
预检请求使用 OPTIONS 方法,包括以下字段:
- Origin: 表明实际请求的源站;
- Access-Control-Request-Method: 表明实际请求所使用的 HTTP 方法;
- Access-Control-Request-Headers: 表明实际请求所额外携带的头部字段;
注:预检请求由浏览器自动发出,无须手动设置这些字段。
预检请求的响应
服务器收到预检请求后,检查了 Origin, Access-Control-Request-Method, Access-Control-Request-Headers 后,可以确认是否允许跨源请求,然后做出回应。
响应包括以下字段:
- Access-Control-Allow-Origin 指定了允许访问该资源的域,含义同简单请求
- Access-Control-Allow-Methods 指定了实际请求中允许使用的所有HTTP方法
- Access-Control-Allow-Headers 指定了实际请求中允许携带的所有首部字段
- Access-Control-Allow-Credentials 指定了实际的请求是否可以使用credentials,含义同简单请求
- Access-Control-Max-Age 指定了预检请求的结果能够被缓存多久,单位秒
正常跨域请求
通过预检后,正常跨域请求跟简单请求一样,浏览器会在请求头添加 Origin 字段;
服务器的回应也跟简单请求一样,必须包含 Access-Control-Allow-Origin 字段。
SprinBoot中跨域的三种解决方法
CrossOrigin注解
//@CrossOrigin 表示所有的URL均可访问此资源
@CrossOrigin(origins = "http://127.0.0.1:8093")//表示只允许这一个url可以跨域访问这个controller
@RestController
@RequestMapping("/testCorss")
public class CorssOriginController {
//可以对方法运用该注解
//@CrossOrigin(origins = "http://127.0.0.1:8093")
@GetMapping("/getString")
public String getString(){
return "跨域成功!";
}
}
@CrossOrigin这个注解用起来很方便,这个可以用在方法上,也可以用在类上。如果你不设置他的value属性,或者是origins属性,就默认是可以允许所有的URL/域访问。
- value属性可以设置多个URL。
- origins属性也可以设置多个URL。
- maxAge属性指定了准备响应前的缓存持续的最大时间。就是探测请求的有效期。
- allowCredentials属性表示用户是否可以发送、处理 cookie。默认为false
- allowedHeaders 属性表示允许的请求头部有哪些。
- methods 属性表示允许请求的方法,默认get,post,head。
实现WebMvcConfigurer
public class MyWebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/testCross/**")
.allowedHeaders("*")
.allowedMethods("*")
.allowCredentials(true)
.allowedOrigins("http://localhost:8093")
.maxAge(2000);
}
}
重写addCorsMappings方法就行,配置好参数,参数和上面的注解的参数类似。这个配置属于全局配置,配置好了全部的接口都遵循此规则。上面的注解方式只对类或者方法生效。addMaping是设置对那种格式的URL生效,也就是跟在URL后面的路径。
过滤器配置
import org.springframework.boot.web.servlet.FilterRegistrationBean;
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 Filter {
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://localhost:8093");//*表示允许所有
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config); // CORS 配置对所有接口都有效
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}
利用过滤器配置实现跨域,还有另外一种方法
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Configuration
public class CorssFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader("Access-Control-Allow-Credentials", "true");
res.addHeader("Access-Control-Allow-Origin", "http://localhost:8093");
res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
res.addHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN");
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}