### 同源策略
> 同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制
同源策略主要有两方面的限制:
* 接口请求
* DOM操作
为什么呢?
限制接口请求的目的是为了防止CSRF攻击,简而言之,就是在用户在正常网页登录状态下,访问了其他攻击性的网页,则导致攻击性网页模拟用户操作行为。
限制DOM操作的目的是为了防止钓鱼网站的攻击;
### JSONP解决跨域问题
首先,准备两个环境:
* 一个是前端环境,通过http-server -p 9100 启动一个html文件,http://localhost:9100
* 服务器环境,通过node+express启动一个小型后端服务,http://localhost:9000
前端代码
```html
<script type='text/javascript'>
window.jsonpCb = function (res,data) {
console.log(res)
console.log(data)
}
</script>
<script src='http://localhost:9000/api/jsonp?msg=helloJsonp&cb=jsonpCb' type='text/javascript'></script>
```
node服务
```js
app.get('/api/jsonp', (req, res) => {
// 这里实际返回的是前端的函数方法自执行
res.send(`${req.query.cb}('${req.query.msg}',${Date.now()})`)
})
```
当然 我们也可以直接使用jquery的ajax中jsonp的方式,但是目前就jquery而言不支持post请求,即使type改为POST也会自动转为GET:
```js
// 注意这里指定的回调函数必须属于window对象
function jsonpCb(res, data) {
console.info("回调occurs");
console.log(res)
console.log(data)
}
$(function () {
$.ajax({
url: 'http://localhost:9000/api/jsonp',
type: 'GET',
dataType: 'jsonp',
jsonpCallback: "jsonpCb",
data: 'msg=helloJsonp',
success: function (res) {
console.log(res)
}
})
});
```
那么如何使用jsonp支持post提交呢,我们使用iframe+form的方式来解决:
这里我们简单点,首先创建一个空的iframe和一个表单:
```html
<iframe src="" name="crossIframe" id="crossIframe" style="display: none;">
</iframe>
<form action="http://localhost:9000/api/post/jsonp" id="crossForm">
<input type="text" name="age" value="100">
<input type="text" name="name" value="zhangsan">
</form>
```
然后我们可直接来提交form表单:
```js
$(function () {
let iframe = document.getElementById('crossIframe')
let form = document.getElementById('crossForm')
form.method = 'POST'
form.target = iframe.name
form.submit()
});
```
服务端我们使用body-parser的中间件:
```js
var bodyParser = require('body-parser')
app.use(bodyParser.json())
app.use(
bodyParser.urlencoded({
extended: true
})
)
app.post('/api/post/jsonp', (req, res) => {
console.log(req.body)
res.send({
success: true,
message: '数据提交成功',
data: req.body //这里把请求数据再传回去
})
})
```
### CORS
> 这是W3C官方的标准了,Cross-origin resource sharing 跨域资源共享,是处理跨域问题的标准处理方法。
这里我们要引入一个概念,简单请求和非简单请求:
> 简单请求:使用设定的请求方式请求数据
> 非简单请求:使用设定的请求方式请求数据之前,先发送一个OPTIONS请求,看服务端是否允许客户端发送非简单请求.
只有"预检"通过后才会再发送一次请求用于数据传输
区别(只有同时满足以下两个条件时,才是简单请求,否则为非简单请求):
* 请求方式:HEAD,GET,POST
* 请求信息:
* Accept
* Accept-Language
* Content-Language
* Last-Event-ID
* Content-Type 对应的值是以下三个中的任意一个
application/x-www-form-urlencoded
multipart/form-data
text/plain
>[info] 虽然可以通过设置响应头和响应方式等支持非简单请求,但是不到万不得已的情况,不能允许客户端发送非简单请求.
因为非简单请求会使服务器比简单请求的多一倍的压力.
不做过度解读吧,但还是把两种方式列出来:
- 简单请求
服务端,开启Access-Control-Allow-Origin
```js
app.all('/cors', function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS')
res.header('Access-Control-Allow-Headers', 'X-Requested-With')
res.header('Access-Control-Allow-Headers', 'Content-Type')
next()
})
// 客户端,直接发请求即可
$.ajax({
url: 'http://localhost:9000/cors',
type: 'GET',
data: 'msg=she',
success: function (res) {
console.log(res)
}
})
```
![](images/screenshot_1532069140030.png)
- 非简单请求
```js
app.all('/cors', function(req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:9100')
res.header('Access-Control-Allow-Credentials', true)
// 非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)
// 这种情况下除了设置origin,还需要设置Access-Control-Request-Method以及Access-Control-Request-Headers
res.header('Access-Control-Request-Method', 'PUT,POST,GET,DELETE,OPTIONS')
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, t')
next()
})
// 客户端,直接发请求即可
$.ajax({
url: 'http://localhost:9000/cors',
type: 'GET',
data: 'msg=she',
credentials: 'include',
// 这里添加额外的headers来触发非简单请求
headers: {
't': 'extra headers'
},
success: function (res) {
console.log(res)
}
})
```
![](images/screenshot_1532070331860.png)
![](images/screenshot_1532070349530.png)
### 代理
代理的方式就不多讲了,一般都是基于Nginx的反向代理,下面给个简单的配置吧:
```js
server {
listen 9000;
server_name localhost;
location / {
root index.htm;
}
location /api {
# 这里讲接口转发到10000端口
proxy_pass http://localhost:1000;
}
}
```
以上jsonp,cors,代理三种方式就是常见的跨域接口的访问方式。下面讲下DOM的操作方式。
### postMessage
这里不劳烦服务端,我们使用http-server 启动两个端口的html,分别是:
- http://localhost:5000 页面A
- http://localhost:7000 页面B
开始之前,我们先了解下`window.postMessage`语法:
```js
window.postMessage(message,targetOrigin,[transfer])
```
A页面代码:
```html
<iframe src="http://localhost:7000" name="crossDomainIframe" frameborder="0"></iframe>
<script>
$(function () {
let iframe = window.frames['crossDomainIframe']
$('#sendButton').on('click', e => {
iframe.postMessage({
href: location.href,
data: 250
}, 'http://localhost:7000')
})
window.addEventListener('message', (e) => {
// 这里一定要对来源做校验
if (e.origin === 'http://localhost:7000') {
console.log(e.data)
}
})
})
</script>
```
B页面:
```js
window.addEventListener('message', (e) => {
console.log(e.data)
// 这里一定要对来源做校验
if (e.origin === 'http://localhost:5000') {
// e.source可以是回信的对象,其实就是http://localhost:7000窗口对象(window)的引用
// e.origin可以作为targetOrigin
e.source.postMessage({
href: location.href,
data: e.data.data * 2
}, e.origin);
}
})
```
### document.domain
这种方式只适合主域名相同,但子域名不同的iframe跨域
- 前端
- C1-Javascript
- H5图片分块和断点续传
- JavascriptPatterns[Stoyanstefanov]
- macotask和microtask
- 前端代码生成器
- 跨域
- 页面回到顶部滚动按钮实现
- C2-CSS
- 浏览器的一些单位
- 盒模型
- 移动端判断横竖屏
- C3-框架
- ReactNative
- 开发环境搭建(安卓篇)
- Vue
- vue+pdfjs使用
- vue+typescript使用实践
- vue+webpack3.x集成typescript
- Vue源码3
- vue源码分析1
- vue源码分析2
- vue笔记
- C4-工具
- git
- Gitlab-CICD
- mock规则
- vscode-settings
- webpack自定义命令,切换代理地址
- 正则表达式
- 深入浅出webpack
- C5-Node
- express
- express源码阅读
- nightmare使用指南
- 爬虫1.0
- C6-微信
- 微信
- C7-Canvas
- 基础API
- 前端随笔笔记
- 后端
- C1-Java
- shiro
- C2-Linux
- ffmpeg
- ITerm
- Linux
- MongoDB安装
- MySql安装
- Ngnix反向代理
- 常见错误
- 备忘
- mac
- 备忘-Work
- 备忘Link
- 服务器资源
- 教程
- Hexo个人博客搭建笔录
- 文档
- CSS编码规范
- 前端编码规范
- 随笔
- 整理
- 正则
- 链接收藏
- 面试
- CodeWars题库
- CodeWars题库(二)
- Java社招面试题
- Java面试
- Web面试
- 前端笔试题
- 笔试题