有时候后端同学明明自己本地调试,接口都是对的,但是一联调前端同学就可以遇到类似这样的问题:
Access to XMLHttpRequest at 'http://192.168.1.12:8888/users' from origin 'http://192.168.1.66:8000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
这个就是告诉你跨域了,要说跨域,那我们就要先说说同源,
同源 就是,一个针对浏览器的东西,对源A的脚本等内容,如何跟另一个源B交互,提出的一些要求,可以隔离潜在恶意文件,也是浏览器最核心、基本的安全功能,阻止某源的js脚本跟另一个源的内容交互。
一个请求里,我们主要关注:域名(或ip)、端口、协议、请求资源地址,如果前三者任一不同,我们就算不同源了,就会被同源策略拦截。
那么拦截了什么了?
(1)Cookie、LocalStorage、IndexedDB等存储性的内容;
(2)DOM节点;
(3)发不了AJAX请求。
怪了,看(3),发不了Ajax请求,那表单呢?经测试发现表单是可以的,分析一下,其拦截的本质是为了阻止用户读取到另一源的内容,Ajax是读取,获取响应,浏览器才会觉得不安全,而表单一般都是提交数据,所以没问题。也就是说同源不能完全阻止CRSF攻击。
接下来我们再说跨域,就是想要访问不属于自己的资源,这个不属于自己的界定就是:域名、端口、协议任一不同。
那么我们想跨域,就要对症下药,怎么绕过同源?大致分为两大类:
1、服务器端设置允许某些域名访问;
(1)自定义CorsFilter
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
//1. 添加 CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//放行哪些原始域
config.addAllowedOrigin("*");
//是否发送 Cookie
config.setAllowCredentials(true);
//放行哪些请求方式
config.addAllowedMethod("*");
//放行哪些原始请求头部信息
config.addAllowedHeader("*");
//暴露哪些头部信息
config.addExposedHeader("*");
//2. 添加映射路径
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",config);
//3. 返回新的CorsFilter
return new CorsFilter(corsConfigurationSource);
}
}
(2)重写 WebMvcConfigurer
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
//是否发送Cookie
.allowCredentials(true)
//放行哪些原始域
.allowedOrigins("*")
.allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
.allowedHeaders("*")
.exposedHeaders("*");
}
}
(3)注解
可以直接用在Controller上或者用在指定方法上。
@RestController
@CrossOrigin(origins = "*")
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "hello world";
}
}
(4)手动设置响应头 (HttpServletResponse)
@RequestMapping("/index")
public String index(HttpServletResponse response) {
response.addHeader("Access-Allow-Control-Origin","*");
return "index";
}
(5)自定义Filter
@Component
public class MyCorsFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
2、客户端自行控制;
前端改用JSONP,或者是用框架封装好的跨域工具都可以;
3、Nginx
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "POST,GET,OPTIONS";
add_header Access-Control-Allow-Headers *;