Access to XMLHttpRequest at 'http://localhost:7777/dev-api/renren-fast/sys/login' from origin 'http://localhost:8001'
has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
前端开发者经常会被这个问题困扰. 下面就来讲个这个问题.
1. CORS本质
跨源资源共享(CORS,或通俗地译为跨域资源共享)是一种基于 HTTP 头的机制,
该机制通过允许服务器标示除了它自己以外的其他源(域、协议或端口),
使得浏览器允许这些源访问加载自己的资源。
跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,
该机制通过浏览器发起一个到服务器托管的跨源资源的"预检"请求。
在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。
发生跨域的条件
简单来讲就是网络访问跨域了. 默认只能进行同源访问. 只要协议、主机、端口有一个不同, 就会跨域.
- http https 跨域
- baidu.com 39.156.66.10 域名\ip跨域
- 主域相同,子域不同 www.a.com script.a.com 跨域
- 测试的时候, localhost\127.0.0.1 也会出现跨域
允许跨域的请求---简单请求
并不是所有的跨域都会默认被拦截.
简单get、head、post(Content-Type 限定为text/plain、multipart/form-data、application/x-www-form-urlencoded)并且没有设置自定义请求头, 默认不会被拦截.
跨域请求的交互方式
相对于简单请求, 跨域请求会先发送一个options请求.
相当于询问服务器, 我能进行XX请求吗? 请求地址、请求头(有点像函数签名)
针对options请求, 服务端添加一些特有的响应头.
2. 浏览器为什么要限制跨域
出于安全性,浏览器限制脚本内发起的跨源 HTTP 请求。
3. 解决方案
1. vue配置代理
这种在开发式比较常用, 一般在vue.config.js中配置
module.exports = {
devServer: {
proxy: {
'/proxyApi': {
target: 'http://demo.renren.io/renren-fast/, // 目标服务器
changeOrigin: true, // 是否改变源
pathRewrite: { '^/proxyApi': '' } // 路径重写
}
}
}
};
2. nginx 处理
部署在同一个域
将前端项目和后端项目放在一起,作为同一个域,当然不存在跨域的问题.
nginx 配置cors
location /dev-api/ {
# 预见请求,单独处理
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'http://localhost:8001';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'token,DNT,X-CustomHeader,Keep-Alive,
User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type
,Authorization';
# add_header 'Access-Control-Allow-Headers' '*'; preflight response not support *.
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
# 其他请求 post get ...
proxy_pass http://127.0.0.1:18080/;
add_header 'Access-Control-Allow-Origin' 'http://localhost:8001';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' '*'; # ok
add_header 'Access-Control-Allow-Credentials' 'true';
}
根据项目不同会有不同的提示. 根据提示进行add_header参数的调整即可.
这个说的是options(preflight response)响应中 Access-Control-Allow-Headers 没有content-type.
这个说的是options(preflight response)响应中 Access-Control-Allow-Origin 没有设置.(ps. 这个只能写具体的域)
这个说的是options(preflight response)响应中 Access-Control-Allow-Headers 没有token.
重要的是不要紧张,一步步调试分析.
3. springmvc 配置
package io.renren.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.maxAge(3600);
}
}
添加上述代码, 进行自动注入即可.
看网上的答案,有的没有写options方法,那肯定不对.
4. gateway 配置
对于使用gateway的项目, 需要将内部项目的cors配置去掉. 否则会进行重复添加.
添加如下代码,配置跨域.
package com.atguigu.gulimail.gateway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// 允许的来源域名
config.setAllowedOriginPatterns(Arrays.asList(
"http://localhost:8001" // 前端请求的域名
// "https://localhost:10086" // 后端服务的域名
));
// 允许的请求方法
config.addAllowedMethod("*");
// 允许的请求头
config.addAllowedHeader("*");
// 允许携带凭证(如 cookies)
config.setAllowCredentials(true);
// 允许的响应头
config.addExposedHeader("*");
// 预检请求的有效期
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 应用 CORS 配置到所有路径
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}