跨域原理--同源策略
所谓"同源"指的是"三个相同",如果这三个有一个不同就会面临跨域的问题
- 协议相同
- 域名相同
- 端口相同
在非同源的情况下主要有三种浏览器行为收到了限制,
- cookie/LocalStorage和IndexDB无法读取
- cookie一般保存了登录信息,如果没有同源策略那么用户可以随意冒充其它用户。
- LocalStorage里会存储一些会话信息为了安全不能被非同源读取。
- IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。之前没有怎么用过,感觉大佬的这一篇写的很好大家可以看一看浏览器数据库 IndexedDB 入门教程
- DOM 无法获得
- DOM无法获取那么也无法使用iframe获取对方的DOM和进行一些操作,也无法进行父子页面之间的通信。
- AJAX 请求不能发送
- 非同源情况下不能提交ajax请求获取不同源接口的值和进行操作。
AJAX跨域解决方法
JSONP
- 网页动态添加<script>元素,向非同源域请求JSON数据,本质上是利用拥有src属性的标签来进行跨域(script、img、iframe等标签),但是这种跨域方式是有局限的,JSONP方式只能为get请求进行跨域,除此之外均无法使用。
前端部分:
<!DOCTYPE html>
<html>
<head>
<title></title>
<script type="text/javascript">
// 得到结果后的回调函数
var Handler = function(data){
console.log(data)
};
// 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
var url = "http:xx.xxx.com?id=111&callback=flightHandler";
// 创建script标签,设置其属性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把script标签加入head,此时调用开始(动态添加的js不影响原本dom和js脚本的加载)
document.getElementsByTagName('head')[0].appendChild(script);
</script>
</head>
<body></body>
</html>
服务端:
//把json传入回调函数,那么前端则可以获得当前的数据
Handler({
"code": "CA1998",
"price": 1780,
"tickets": 5
});
利用ajax的话:
<script type="text/javascript">
$(document).ready(function(){
$.ajax({
type: "get",
url: "http:xx.xxx.com?id=111",
dataType: "jsonp",
//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
jsonp: "callback",
//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据
jsonpCallback:"Handler",
success: function(json){
console.log(data);
},
error: function(){}
});
});
</script>
CORS 跨域资源共享
nginx转发进行跨域
因为很多时候有可能会出现服务端无法设置,也无法将JSON字符串包装成JSONP可以调用的形式,例如调用国家平台的接口等操作,这时需要想办法让请求接口和开发场景同源。
下载nginx
地址:nginx下载
准备一个需要跨域的demo.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<!-- -->
</body>
<script src="https://www.jq22.com/jquery/jquery-1.10.2.js"></script>
<script type="text/javascript">
var settings = {
"url": "http://check.vecc-mep.org.cn:8090/api/v10/login",//本地调用之后发现跨域了
"method": "POST",
"timeout": 0,
"headers": {
"Content-Type": "application/json"
},
"data": JSON.stringify({"password":"XXXX","username":"XXXX"}),
};
$.ajax(settings).done(function (response) {
console.log(response);
});
</script>
</html>
然后发现跨域了,山的那边海的那边不让我去..
修改nginx/conf/nginx.conf文件如下(建议不要直接复制粘贴,按照注释自己改,我这里并不是整个文件):
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
# 直接请求nginx也是会报跨域错误的这里先设置允许跨域
# 如果代理地址已经允许跨域则不需要这些, 否则报错(虽然这样nginx跨域就没意义了)
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
#这里是根路径,指向了index.html
#(请将你的index.html文件放在nginx/html文件下替换原有的index.html,
#或者你的html名称不是index的话记得来这里改一下)
location / {
root html;
index index.html index.htm;
}
# 这里的api是自定义的名字,
#代表检测到api这个路径之后会转发到我想要的地址,实现"山不过来我就过去"
location api {
rewrite ^/b/(.*)$ /$1 break; # 去除本地接口/api前缀, 否则会出现404
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 转发地址,即我想请求的对方的地址
proxy_pass http://check.vecc-mep.org.cn:8090/api/v10;
}
#error_page 404 /404.html;
......
}
然后修改之后的index.html文件如下:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<!-- -->
</body>
<script src="https://www.jq22.com/jquery/jquery-1.10.2.js"></script>
<script type="text/javascript">
var settings = {
"url": "api/login",//修改为api(我自定义的辣个前缀地址)
"method": "POST",
"timeout": 0,
"headers": {
"Content-Type": "application/json"
},
"data": JSON.stringify({"password":"XXXX","username":"XXXX"}),
};
$.ajax(settings).done(function (response) {
console.log(response);
});
</script>
</html>
先双击nginx进行启动,然后在浏览器输入127.0.0.1,如果出来了你写的index.html页面,f12即可看到结果.
可能出现的错误
发现输入127.0.0.1之后显示无法访问此网站,就是没启动起来
-->先检查一下nginx的路径是否有中文,有中文可能会导致无法启动
-->检查完之后发现不是中文原因,进入nginx/logs/error.log查看错误日志,可以搜索对应的错误信息获取错误原因
-->还是找不出来,建议使用命令行打开之后输入start nginx再试试,如果logs文件夹内出现了pid.log和access.log一般就是成功了
常用nginx命令
-
start nginx 或者 nginx.exe --启动nginx
-
nginx.exe -s stop 快速停止nginx,不保存信息
-
nginx.exe -s quit 完整停止nginx,保存信息
-
nginx.exe -s reload 修改配置信息之后重新载入配置
location后面的配置问题
localtion / {
# 以下规则匹配所有请求
# 因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求
# xxx 你的配置写在这里
}
location = / {
# 精确匹配 / ,后面带任何字符串的地址都不符合
}
localtion /api {
# 匹配任何 /api 开头的URL,包括 /api 后面任意的, 比如 /api/getList
# 匹配符合以后,还要继续往下搜索
# 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
}
localtion ~ /api/abc {
# 匹配任何 /api/abc 开头的URL,包括 /api/abc 后面任意的, 比如 /api/abc/getList
# 匹配符合以后,还要继续往下搜索
# 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
}
总结
nginx还有很多很棒的功能包括负载均衡等,本次主要是提及一下服务端无法设置的情况下利用nginx进行转发跨域的问题,错误之处希望大家积极指正,谢谢大家呀。
参考资料:浏览器同源政策及其规避方法