java service/web前端解决跨域( CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.)
先给两个不从代码层面的解决方案,在客户端/服务器上安装 Nginx 代理软件
或者 Allow CORS: Access-Control-Allow-Origin 浏览器插件
1、什么是跨域?
跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制。
所谓同源是指,域名,协议,端口均相同,只要有一个不同,就是跨域。如:
http://www.123.com/index.html 调用 http://www.123.com/server.php (非跨域)
http://www.123.com/index.html 调用 http://www.456.com/server.php (主域名不同:123/456,跨域)
http://abc.123.com/index.html 调用 http://def.123.com/server.php (子域名不同:abc/def,跨域)
http://www.123.com:8080/index.html 调用 http://www.123.com:8081/server.php (端口不同:8080/8081,跨域)
http://www.123.com/index.html 调用 https://www.123.com/server.php (协议不同:http/https,跨域)
请注意:localhost
和127.0.0.1
虽然都指向本机,但也属于跨域。
浏览器执行javascript
脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。
CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
2、跨域会阻止什么操作?
浏览器是从两个方面去做这个同源策略的,一是针对接口的请求,二是针对Dom的查询。
- 1.阻止接口请求比较好理解,比如用
ajax
从http://192.168.100.150:8020/实验/jsonp.html
页面向http://192.168.100.150:8081/zhxZone/webmana/dict/jsonp发起请求,由于两个url
端口不同,所以属于跨域,在console打印台会报No 'Access-Control-Allow-Origin' header is present on the requested resource
值得说的是虽然浏览器禁止用户对请求返回数据的显示和操作,但浏览器确实是去请求了,如果服务器没有做限制的话会返回数据的,在调试模式的network中可以看到返回状态为200,且可看到返回数据
- 2.阻止
dom
获取和操作
比如a页面中嵌入了iframe
,src
为不同源的b页面,则在a中无法操作b中的dom
,也没有办法改变b中dom
中的css
样式。
而如果ab是同源的话是可以获取并操作的。
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
iframe {
width: 100%;
height: 800px;
}
</style>
</head>
<body>
<!--<iframe src="http://192.168.100.150:8081/zhxZone/webmana/attachment/imageManager" frameborder="0" id="iframe"></iframe>-->
<iframe src="http://192.168.100.150:8020/实验/jsonp.html" frameborder="0" id="iframe"></iframe>
<script type="text/javascript">
var i = document.getElementById("iframe");
i.onload = function () {
/*console.log(i.contentDocument)
console.log(i.contentWindow.document.getElementById("text").innerHTML)*/
var b = i.contentWindow.document.getElementsByTagName("body")[0];
i.contentWindow.document.getElementById("text").style.background = "gray";
i.contentWindow.document.getElementById("text").innerHTML = "111";
}
</script>
</body>
</html>
甚至是可以获取iframe
中的cookie
var i=document.getElementById("iframe");
i.onload=function(){
console.log(i.contentDocument.cookie);
}
不用说也知道这是极为危险的,所以浏览器才会阻止非同源操作dom
。
浏览器的这个限制虽然不能保证完全安全,但是会增加攻击的困难性
虽然安全机制挺好,可以抵御坏人入侵,但有时我们自己需要跨域请求接口数据或者操作自己的dom
,也被浏览器阻止了,所以就需要跨域。
跨域的前提肯定是你和服务器是一伙的,你可以控制服务器返回的数据,否则跨域是无法完成的 。
3、解决跨域
如果是前端的调试,推荐使用前端跨域插件 浏览器插件
一、服务器端
1.1、后端配置解决跨域 两种方式
CORS
背后的基本思想是使用自定义的HTTP头部允许浏览器和服务器相互了解对方,从而决定请求或响应成功与否.
这其实和第2中方法(后台配置)基本相同,都是通过过滤器在response中返回头部,使服务器和浏览器可互通。
Access-Control-Allow-Origin:指定授权访问的域
Access-Control-Allow-Methods:授权请求的方法(GET, POST, PUT, DELETE,OPTIONS
等)
f适合设置单一的(或全部)授权访问域,所有配置都是固定的,特简单。也没根据请求的类型做不同的处理
方式一:重写一个过滤器CORSFilter
@Component
public class CORSFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//只允许 http://localhost:9527 站点上的资源跨域
//response.addHeader("Access-Control-Allow-Origin", "http://localhost:9527");
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.addHeader("Access-Control-Allow-Headers", "Content-Type");
response.addHeader("Access-Control-Max-Age", "1800");//1800s == 30 min
filterChain.doFilter(request, response);
}
}
原因是请求中含有其他参数
解决:放行所有原始域
response.addHeader("Access-Control-Allow-Headers", "Content-Type");
改为 response.addHeader("Access-Control-Allow-Headers", "*");
//response.addHeader("Access-Control-Allow-Headers", "Content-Type");
response.addHeader("Access-Control-Allow-Headers", "*");
或者
加上 Authorization
和userid
response.setHeader("Access-Control-Allow-Headers", "Authorization");
response.setHeader("Access-Control-Allow-Headers", "userid");
或者
在web请求的位置去掉这两个请求头参数
import mesConfig from '@/utils/globalConfig'
import axios from 'axios'
//本地请求对象
const localService = axios.create({
baseURL: mesConfig.localURL
})
// axios.defaults.headers.common['Authorization'] = 'Bearer' + sessionStorage.getItem('access_token')
// axios.defaults.headers.common['userid'] = sessionStorage.getItem('id')
localService.interceptors.request.use(
config => {
return config
},
err => {
return Promise.reject(err)
}
)
方式二:springboot
配置注解
@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 configSource = new UrlBasedCorsConfigurationSource();
//允许 所有 站点上的资源跨域
configSource.registerCorsConfiguration("/**", config);
//3.返回新的CorsFilter.
return new CorsFilter(configSource);
}
}
方式三: 通过Nginx
反向代理
上个方法跨域是借助了浏览器对 Access-Control-Allow-Origin
的支持。但有些浏览器是不支持的,所以这并非是最佳方案,现在我们来利用nginx
通过反向代理 满足浏览器的同源策略实现跨域!
这里是一个nginx
启用COSR
的参考配置
#
# Wide-open CORS config for nginx
#
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
#
# Custom headers and headers various browsers *should* be OK with but aren't
#
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
}
springboot 解决跨域- SegmentFault 思否
二、web前端
1、方法:
1.1、前端方法就用jsonp
使用
JSONP
主要是目的通过动态创建Script,动态拼接url
,进而抓取数据,实现跨域。这篇文章主要介绍了VUE2.0
中Jsonp
的使用方法(前端),需要的朋友可以参考下
jsonp
是前端解决跨域最实用的方法。
原理就是html
中 的link
,href
,src
属性都是不受跨域影响的,link可以调用远程的css
文件,href
可以链接到随便的url
上,图片的src
可以随意引用图片,script的