### 同源策略
> 同源策略限制了從同一個源加載的文檔或腳本如何與來自另一個源的資源進行交互。這是一個用于隔離潛在惡意文件的重要安全機制
同源策略主要有兩方面的限制:
* 接口請求
* 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)
}
})
```

- 非簡單請求
```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)
}
})
```


### 代理
代理的方式就不多講了,一般都是基于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面試
- 前端筆試題
- 筆試題