小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
1、什么是跨域
演示跨域:
把昨天的ajax访问node.js编写的后台server.js
然后注释掉server.js中的允许跨域代码
server.js:
/*
实现注册登录
接口使用:
接口地址:http://127.8.0.1: 3001/user接受参数:
act: login/登录 reg/注册
user:用户名
pass:密码
*/
var http = require('http');
var fs = require('fs');
var querystring = require('querystring');
var url = require('url');
var user = {} ;//act 判断登录还是注册,user /pass
var server = http.createServer(function (req, res) {
//设置允许跨域的域名,*代表允许任意域名跨域
/* res.setHeader(
'Access-Control-Allow-Origin',"*"
) */
const GET = url.parse(req.url, true);
var query = GET.query;
var pathname = GET.pathname;
var str = '';
if (pathname == '/user') {
req.on('data', function (chunk) {
console.log(11)
str += chunk;
});
req.on('end', function () {
var POST = querystring.parse(str)
switch (POST.act) {
case 'reg':
if (user[POST.user] == null) {
user[POST.user] = POST.pass;
res.write('{"ok":true,"msg":"注册成功"}')
} else {
res.write('{"ok":false,"msg":"该用户名已经被注册"}')
}
break;
case 'login':
if (user[POST.user] == null) {
res.write('{"ok":false,"msg":"用户名不存在"}')
} else if (user[POST.user] != POST.pass) {
res.write('{"ok":false,"msg":"用户名或密码不对"}')
} else {
res.write('{"ok":true,"msg":"登录成功"}')
}
break;
default:
res.write('你要干啥?')
}
res.end()
})
} else {
const file_name = './www' + req.url;
fs.readFile(file_name, 'utf-8', function (err, data) {
if (err) {
res.write('404')
} else {
res.write(data)
}
res.end()
})
}
}).listen(3001, 'localhost',function () {
console.log('your server is running here:lhocalhost:3001')
})
login_server_demo.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
用户名:<input type="text" name="user" id="user">
<br>
密码: <input type="password" name="pass" id="pass">
<br>
<input type="submit" value="注册" onclick="register()">
<input type="submit" value="登录" onclick="login()">
</body>
<script>
function register() {
var user_ = document.getElementById('user').value;
var pass_ = document.getElementById('pass').value;
//1、创建对象
var ajax_ = new XMLHttpRequest() || new ActiveXObject('Microsoft.XMLHTTP');
//2、创建连接
ajax_.open('post','http://127.0.0.1:3001/user',true);
//3、发送请求
//拼接数据
var data = 'act=reg&user='+user_+'&pass='+pass_;
ajax_.send(data);
//4、接收服务器数据
ajax_.onreadystatechange = function(){
console.log(ajax_.readyState);
if(ajax_.readyState==4){
if(ajax_.status==200){
console.log(ajax_.responseText);
}else{
console.log('erro');
}
}
}
}
// 登录部分
function login(){
var user = document.getElementById('user').value;
var pass = document.getElementById('pass').value;
var ajax_ = new XMLHttpRequest()|| new ActiveObject('Microsoft.XMLHTTP');
ajax_.open('post','http://127.0.0.1:3001/user',true);
var date = 'act=login&user='+user+'&pass='+pass;
ajax_.send(date);
ajax_.onreadystatechange=function(){
if(ajax_.readyState==4){
if(ajax_.status==200){
console.log(ajax_.responseText);
}else{
console.log('erro');
}
}
}
}
</script>
</html>
此时显示错误信息如下:
该错误表示:
从原产地“http://127.0.0.1:5500”访问“http://127.0.0.1:3001/user”的XMLHttpRequest已被CORS策略阻止:被请求的资源上没有“Access- control - allow - origin”报头。
CORS:
CORS是一个w3c标准,全称是:跨域资源共享(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制
跨域,指的是浏览器不能执行其他网站的脚本 简单地理解就是因为JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象 例子:比如淘宝网不能请求京东的数据
产生跨域的原因:
由浏览器的同源策略造成的
同源策略,它是由Netscape提出的一个著名的安全策略。 现在所有支持JavaScript 的浏览器都会使用这个策略。
同源策略大白话:
生活中,社会能够安定的运行,有一个前提,大家都默认遵守一个协定:每个人只能自由的出入自己的家,拿取自己家的东西。而如果大家都不遵循这种协定,你可以随便去拿别人家的东西,别人也可以随便的出入你的家,这样社会就乱套了。此处大家都遵循的各用各家的东西,就叫做同源策略
所谓同源是指,域名,协议,端口相同
域名和ip地址:
比方说,我们要去逛永辉超市,在导航里面输入永辉超市,导航会找到永辉超市的地址,在某某街某某号,并导航到这个位置。
这里我们输入永辉超市就好比输入一个域名,但是系统会将它转换为具体的街道门牌号,这个就好比网络上的ip地址,是精确且唯一的。
判断以下内容是否能够正常访问:
2、跨域的限制
随着互联网的发展,同源政策越来越严格。目前,如果非同源,共有三种行为受到限制。
- 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB。【缓存数据】
- 无法接触非同源网页的 DOM。
- 无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)
查看LocalStorage:
右键 ,检查,application,localStroage
3、跨域解决方案
1、设置CORS跨域资源共享
后台在请求头信息里面添加:服务端:、
response.setHeader("Access-Control-Allow-Origin", "*");
“*”表示所有的域都可以接受 CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能。
2、Ajax 跨域请求
由于在工作中需要使用AJAX请求其他域名下的资源,但是会出现拒绝访问的情况,这是因为基于安全的考虑,AJAX只能访问本地,同域的资源,而不能跨域访问 可以让服务器去别的网站获取内容然后返回页面
大白话:
我们要租房子,房源被中介给垄断了,我们无法直接找到房东。
我们先找到中介,中介去和房东谈具体事宜,谈好以后把每个月多少钱的信息告诉我们
3、Jsonp
例:
通过img的src属性,访问百度的图片:
<img src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1910846161,216642357&fm=26&gp=0.jpg" alt="">
jsonp就是利用script标签的跨域能力请求资源
Jsonp 实现思路:
- 全称是 JSON with Padding,请求时通过动态创建一个 Script,在 Script 中发出请求,
- 通过这种变通的方式让请求资源可以跨域。
- 它不是一个官方协议,是一个约定,约定请求的参数里面如果包含指定的参数(默认是 callback),就说明是一个 JSONP 请求,服务器发现是 JSONP 请求,就会把原来的返回对象变成 JS 代码。
- JS 代码是函数调用的形式,它的函数名是 callback 的值,它的函数的参数就是原来需要返回的结果。
- 后台会把函数调用,重新返回给前端
json_01.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模拟jsonp</title>
</head>
<body>
<script>
// 调用后台函数
function fn(value){
console.log(value);
}
</script>
<!--
通过script的src属性进行跨域
传递的参数默认是callback
后台看到callback就能够判断出来是一个jsonp请求
会根据callback的值,自动生成函数,函数名就是callback的值
后台把数据封装成对象,作为参数传递给函数作为值
-->
<script src="./testJs.js?callback=fn"></script>
</body>
</html>
testjs.js
// testjs.js就是我们模拟的后台
// 后台能判断出来是否是jsonp请求
// 如果是,就会生成一个函数的调用,而函数名,就是前端提交上来的callback的值在假如我们要给前端返回一个数据
var data = {
"name":"张三",
age : 20,
address:"郑州"
}
// 在后台生成了函数的调用,把数据以参数的形式,进行传递
// 后台会把这个函数的调用,重新返回给前端
fn(data);
使用node.js模拟jsonp
1、找到后台文件,jsonp_server.js,复制进项目
/*
jsonp 接口地址:
http: //localhost:3003/jsonp?callback=test
传参数
约定请求的参数中的指定参数:callback
服务器发现是 JSONP 请求, 就会把原来的返回对象变成 JS 代码。
JS 代码是函数调用的形式, 它的函数名是 callback 的值,
它的函数的参数就是原来需要返回的结果。
*/
//通过require将http库包含到程序中
let http = require('http');
//引入url模块解析url字符串
let url = require('url');
//引入querystring模块处理query字符串
let querystring = require('querystring');
//创建新的HTTP服务器
let server = http.createServer();
//通过request事件来响应request请求
server.on('request', function (req, res) {
let urlPath = url.parse(req.url).pathname;
let qs = querystring.parse(req.url.split('?')[1]);
// 如果请求路径里面包含有jsonp 和callback 就判断出来这是一个jsonp请求
if (urlPath === '/jsonp' && qs.callback) {
res.writeHead(200, {
'Content-Type': 'application/json;charset=utf-8'
});
// 此处的data数据 就是后台要返回个前端的数据
let data = {
"msg": "jsonp返回的数据",
"fn": qs.callback
};
data = JSON.stringify(data);
// 根据callback的值,创建函数 data 是函数内传递的参数
let callback = qs.callback + '(' + data + ');';
console.log(callback);
// 向前端进行响应
res.end(callback);
} else {
res.writeHead(200, {
'Content-Type': 'text/html;charset=utf-8'
});
res.end('Hell World\n');
}
});
server.listen(3003,'localhost',function(){
console.log('your server is running here:localhost:3003');
});
2、使用node命令启动文件
node jsonp_server.js
3、创建html文件,jsonp_server.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
function test(v){
console.log(v);
}
</script>
<!-- callback:是后台定好的,如果规定是其他的,例如backcall / cb你在传参数是,就要按规定来,传bcakcall /cb
test:具体的参数的值,可以根据后台的规定来定义
例如:后台规定参数的值,必须叫test那你就只能写testI -->
<script src="http://localhost:3003/jsonp?callback=test"></script>
</body>
</html>
效果图:
JSONP优缺点:
优点:兼容性强&不受同源策略的限制 缺点:只能用get方法,不能使用post方法
因为get请求方式把请求参数放在了url地址中,后台解析jsonp是通过url的callback参数进行判断的,所以之支持get请求
优化jsonp.html,动态创建script的src属性:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button onclick="getJsonp()">获取jsonp</button>
<script>
function test(v){
console.log(v);
}
function getJsonp(){
var script_ = document.createElement('script');
script_.src = 'http://localhost:3003/jsonp?callback=test';
document.body.appendChild(script_);
//清除页面上的script标签
if(script_){
document.body.removeChild(script_);
}
}
</script>
</body>
</html>
4、WebSocket 【了解】
WebSocket 是一种通信协议,和http,https是同一级别的。
使用ws://(非加密)和wss://(加密)作为协议前缀。
该协议不实行同源政策,只要服务器和浏览器支持,就可以通过它进行跨源通信。 缺点 只有在支持websocket协议的服务器和浏览器上才能正常工作
websocket不支持ie6以下的浏览器
webSocket在线测试网站:
菜鸟教程详解: