同源与跨域
1. 同源
同源策略的基本概念
1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。同源策略:最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源"。所谓"同源"指的是"三个相同"。
协议相同
域名相同
端口相同
举例来说,这个网址http://www.example.com/dir/page.html
协议是http://
,
域名是www.example.com
,端口是80
(默认端口可以省略)。它的同源情况如下。
http://www.example.com/dir2/other.html:同源
file:///F:/phpStudy/WWW/day01/04-demo/04.html 不同源(协议不同)
http://v2.www.example.com/dir/other.html:不同源(域名不同)
http://www.example.com:81/dir/other.html:不同源(端口不同)
同源策略的目的
同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。
同源策略的限制范围
随着互联网的发展,“同源策略”越来越严格,目前,如果非同源,以下三种行为都将收到限制。
1. Cookie、LocalStorage 和 IndexDB 无法读取。
2. DOM 无法获得。
3. AJAX 请求不能发送。
虽然这些限制是很有必要的,但是也给我们日常开发带来不好的影响。比如实际开发过程中,往往都会把服务器端架设到一台甚至是一个集群的服务器中,把客户端页面放到另外一个单独的服务器。那么这时候就会出现不同源的情况,如果我们知道两个网站都是安全的话,我们是希望两个不同源的网站之间可以相互请求数据的。这就需要使用到跨域 。
2. 跨域
jsonp( 无兼容性问题 )
JSONP(JSON with Padding)、可用于解决主流浏览器的跨域数据访问的问题。
原理:服务端返回一个定义好的js函数的调用,并且将服务器的数据以该函数参数的形式传递过来,这个方法需要前后端配合
script
标签是不受同源策略的限制的,它可以载入任意地方的 JavaScript 文件。类似的还有img
和link
标签
<!--不受同源策略限制的标签-->
<img src="http://www.api.com/1.jpg" alt="">
<link rel="stylesheet" href="http://www.api.com/1.css">
<script src="http://www.api.com/1.js"></script>
jsonp演化过程1
php文件
header("content-type:text/html;charset=utf-8");
echo "alert(1111)";
html文件
<script src="http://www.api.com/testjs.php"></script>
原理:其实src的路径是什么文件不重要,无论引入js文件还是php文件,最后返回给浏览器的都是字符串,因此我们script标签是可以引入一个php文件的。
jsonp演化过程2
php文件
header("content-type:text/html;charset=utf-8");
echo "var a = 118;";
html文件
<script src="http://www.api.com/testjs.php"></script>
<script>
//a打印出来了118
console.log(a);
</script>
我们现在做到了一件事情,从不同源的php文件中获取到了数据
缺点:获取数据的script标签必须写在使用的script标签的前面,必须保证先有数据才能对数据进行渲染。
jsonp演化过程3
php代码
header("content-type:text/html;charset=utf-8");
$arr = array(
"name"=>"zs",
"age"=>18
);
$result = json_encode($arr);
//这是一段js函数的调用的代码,$result就是我们想要的数据
echo "func($result)";
js代码
<script>
function func(data) {
console.log(data);
}
</script>
<script src="http://www.api.com/testjs.php"></script>
缺点:后端必须知道前端声明的方法的名字,后端才能调用。
jsonp演化过程4
php代码
header("content-type:text/html;charset=utf-8");
$arr = array(
"name"=>"zs",
"age"=>18
);
$result = json_encode($arr);
//这是一段js函数的调用的代码,$result就是我们想要的数据
echo $_GET['callback']."($result)";
// jsonp: json width padding
//echo "$callback($result)";
javascript代码
function fun(data) {
console.log(data);
}
var button = document.querySelector("button");
button.onclick = function () {
var script = document.createElement("script");
script.src = "http://www.api.com/testjs.php?callback=fun";
document.body.appendChild(script);
}
-
jsonp的原理就是 借助了script标签 src 请求资源时, 不受同源策略的限制.
-
在服务端返回一个函数的调用,将数据当前调用函数的实参。
-
在浏览器通过形端,需要程序要声明一个全局函数,参就可以获取到服务端返回的对应的值
jquery对于jsonp的封装
//使用起来相当的简单,跟普通的get请求没有任何的区别,只需要把dataType固定成jsonp即可。
$.ajax({
type:"get",
url:"http://www.Jepson.com/testjs.php",
dataType:"jsonp",
data:{
uname:"Jepson",
upass:"123456"
},
success:function (info) {
console.log(info);
}
});
【案例:查询天气.html】 [天气查询api地址](https://www.jisuapi.com/api/weather/)
【案例:省市区三级联动.html】 api地址 (https://www.jisuapi.com/api/area/)
//天气.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
table {
margin-top: 20px;
width: 500px;
border: 1px solid #000;
border-collapse: collapse;
}
td {
height: 30px;
line-height: 30px;
border: 1px solid #000;
}
</style>
</head>
<body>
<input type="text" id="city">
<input type="button" value="查询" id="btn">
<table id="showTable">
</table>
<script src="template-web.js"></script>
<script src="jquery-1.12.4.js"></script>
<script type="text/html" id="tpl">
<tr>
<td>城市</td>
<td>{{ result.city }}</td>
</tr>
<tr>
<td>日期</td>
<td>{{ result.date }}</td>
</tr>
<tr>
<td>天气</td>
<td>{{ result.weather }}</td>
</tr>
<tr>
<td>气温</td>
<td>{{ result.temp }}度</td>
</tr>
<tr>
<td>最高气温</td>
<td>{{ result.temphigh }}度</td>
</tr>
<tr>
<td>最低气温</td>
<td>{{ result.templow }}度</td>
</tr>
<tr>
<td>更新时间</td>
<td>{{ result.updatetime }}</td>
</tr>
</script>
<script>
// 思路: 1. 点击按钮, 获取文本框的内容, 发送 jsonp 请求(跨域了), 请求数据,
// 2. 得到数据, 通过模板引擎来渲染
$(function() {
$('#btn').click(function() {
$.ajax({
type: "get",
url: "http://api.jisuapi.com/weather/query",
data: {
appkey: "3fa977031a30ffe1",
city: $('#city').val()
},
dataType: "jsonp",
success: function( info ) {
console.log( info )
// 模板 id, 数据对象
var htmlStr = template( "tpl", info );
$('#showTable').html( htmlStr );
}
});
});
})
</script>
</body>
</html>
//省市区.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
div {
margin: 50px;
}
</style>
</head>
<body>
<div>
<select id="s1">
<option>请选择</option>
</select>
<select id="s2">
<option>请选择</option>
</select>
<select id="s3">
<option>请选择</option>
</select>
</div>
<script src="jquery-1.12.4.js"></script>
<script src="template-web.js"></script>
<script type="text/html" id="tpl">
<option>请选择</option>
{{ each result v i }}
<option value="{{ v.id }}">{{ v.name }}</option>
{{ /each }}
</script>
<script>
// 两个核心点
// 1. 监听 select 变化, 用change
// 2. 获取选中的内容 id, 通过 select 的 value 来获取
// 思路:
// 1. 一进入页面, 请求省的数据
// 2. 用户选择了省, 请求市的数据
// 3. 用户选择了市, 请求区的数据
$.ajax({
type: "get",
url: "http://api.jisuapi.com/area/province",
data: {
appkey: "3fa977031a30ffe1" // 用户唯一标识, 每个人的个人中心可以查看
},
dataType: "jsonp",
success: function( info ) {
console.log( info );
var htmlStr = template( "tpl", info );
$('#s1').html( htmlStr );
}
});
// change 事件, 当select被选择的内容改变时触发
$('#s1').change(function() {
// 重置市和区的数据
$('#s2').html("<option>请选择</option>");
$('#s3').html("<option>请选择</option>");
// console.log( "选择的内容改变了" );
// 发送 ajax 请求, 请求市的数据
$.ajax({
type: "get",
url: "http://api.jisuapi.com/area/city",
data: {
parentid: $('#s1').val(), // 获取选中的省的 id
appkey: "3fa977031a30ffe1"
},
dataType: "jsonp",
success: function( info ) {
var htmlStr = template( "tpl", info );
$('#s2').html( htmlStr );
}
})
});
$('#s2').change(function() {
// 发送ajax请求, 获取区的数据
$.ajax({
type: "get",
url: "http://api.jisuapi.com/area/town",
data: {
parentid: $('#s2').val(), // 获取选中的城市 id
appkey: "3fa977031a30ffe1"
},
dataType: "jsonp",
success: function( info ) {
var htmlStr = template( "tpl", info );
$('#s3').html( htmlStr );
}
})
})
</script>
</body>
</html>