工作上遇到一個小問題, 就是桌面軟件里有個打開瀏覽器獲取cookie的功能, 這個功能C#里可能就是打開一個webview, 然后通過api獲取頁面cookie. 但是在網頁端就很坑了, 放iframe也不行, 畢竟打開的頁面不是可控的, 無法通信, 存在跨域問題.
實現(xiàn)代碼: https://github.com/klren0312/cookies-chrome-plugin/edit/master/README.md
原理
如果非要獲取, 只能用瀏覽器插件或者套個Electron, 當然還是用瀏覽器插件啦.瀏覽器插件, 通過右鍵點擊發(fā)送, 可以將獲取的cookie和ua發(fā)送到需要的頁面.
首先插件會在每個頁面創(chuàng)建一個id為'content-block'的DOM
, 然后主頁面會通過postMessage
, 通知插件獲取主頁面的tabId
, 隨后, 進入需要獲取cookie
和ua
的頁面, 右鍵獲取, 然后通過之前緩存的主頁面tabId
將獲取的cookie
和ua
發(fā)送到content.js
, content.js
將cookie
和ua
組成的json
寫入id
為'content-block'的DOM
, 主頁面通過mutationObserver
監(jiān)聽id
為'content-block'的DOM
的變化, 觸發(fā)數(shù)據獲取
實現(xiàn)
1. 谷歌瀏覽器插件基本結構
前端內容(content.js), 后臺處理(utils.js), 插件彈框(popup.js, popup.html), 以及配置文件(manifest.json). 前面三個JS文件名稱都是自定義的, 需要在配置文件中配置
2.配置文件
{
"name": "Cookie與UserAgent獲取",
"description": "輔助抓取網站登陸后有效Cookies和UserAgent",
"version": "0.0.3",
"author": "ZZES",
"homepage_url": "https://github.com/klren0312/cookies-chrome-plugin",
"permissions": [
"contextMenus",
"tabs",
"cookies",
"<all_urls>"
],
"icons": {
"16": "icon-16.png",
"48": "icon-48.png",
"128": "icon-128.png"
},
"background": {
"scripts": [
"utils.js"
]
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["content.js"],
"all_frames":true,
"run_at": "document_start"
}],
"browser_action": {
"default_icon": "icon-16.png",
"default_title": "Cookie與UserAgent獲取",
"default_popup": "popup.html"
},
"manifest_version": 2
}
主要問題解讀
1. permissions
權限介紹可以查看: https://developer.chrome.com/extensions/permissions
權限, 需要操作一些瀏覽器功能, 就需要列出權限, 比如需要在右鍵菜單使用插件, 就開啟contenMenus
; 需要獲取tab頁信息, 就開啟tabs
; 需要獲取瀏覽器cookie, 就開啟cookies
;最后是插件的應用域名, 這個如果想在所有域名下運用, 就使用<all_urls>
2.background
后臺相關處理腳本, 幕后工作者, 進行一些瀏覽器相關操作
3.content_scripts
前臺相關操作, 比如DOM操作
4.browser_action
就是瀏覽器插件那塊的圖標和彈框
3.background代碼
let mainPageId = null
// 將當前頁面的cookies復制到剪切板
function copyCookies(info, tab) {
let cookies = ''
chrome.cookies.getAll({
url: tab.url
}, function (cookie) {
// 遍歷當前域名下cookie, 拼接成字符串
cookie.forEach(v => {
cookies += v.name + "=" + v.value + ";"
})
// 添加到剪切板
const input = document.createElement('input')
input.style.position = 'fixed'
input.style.opacity = 0
input.value = cookies
document.body.appendChild(input)
input.select()
document.execCommand('Copy')
document.body.removeChild(input)
})
}
// 將當前頁面的UA復制到剪切板
function copyUA () {
const input = document.createElement('input')
input.style.position = 'fixed'
input.style.opacity = 0
input.value = navigator.userAgent
document.body.appendChild(input)
input.select()
document.execCommand('Copy')
document.body.removeChild(input)
}
// 發(fā)送Cookies和UA到主頁面
function sendCookieAndUA (info, tab) {
let cookies = ''
const ua = navigator.userAgent
chrome.cookies.getAll({
url: tab.url
}, function (cookie) {
// 遍歷當前域名下cookie, 拼接成字符串
cookie.forEach(v => {
cookies += v.name + "=" + v.value + ";"
})
// 如果存在主頁面的 tabId, 則將當前頁的cookies發(fā)送給主頁面
let sendId = mainPageId ? mainPageId : tab.id
chrome.tabs.sendMessage(sendId, {
cookies: cookies,
ua: ua
}, function(response) {
console.log(response)
})
})
}
// 給popup使用的方法
// 獲取頁面ID
function popupGetTabId () {
return mainPageId
}
// 清除頁面ID
function popupCleanTabId () {
mainPageId = null
}
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
// https://zhuanlan.zhihu.com/p/57820028
if (request !== 'ok') {
if (request.type === 'tab') {
if (request.level === 'main') { // 如果頁面是主頁面, 則將其 tabId 緩存, 并發(fā)送給主頁面
mainPageId = sender.tab.id
sendResponse({type: 'tab', level: 'main', tabId: sender.tab.id})
}
} else if (request.type === 'cookies') {
let cookies = ''
chrome.cookies.getAll({
url: request.target
}, function (cookie) {
// 遍歷當前域名下cookie, 拼接成字符串
cookie.forEach(v => {
cookies += v.name + "=" + v.value + ";"
})
sendResponse({type: 'cookies', cookies: cookies})
})
}
}
}
)
var parent = chrome.contextMenus.create({
"title": "Cookie與UserAgent獲取",
"contexts": ["page"]
})
var copyCookie = chrome.contextMenus.create({
"title": "提取Cookies至剪切板",
"parentId": parent,
"contexts": ["page"],
"onclick": copyCookies
})
var copyUA = chrome.contextMenus.create({
"title": "提取UserAgent至剪切板",
"parentId": parent,
"contexts": ["page"],
"onclick": copyUA
})
var sendCookieAndUA = chrome.contextMenus.create({
"title": "發(fā)送Cookies和UA到主頁面",
"parentId": parent,
"contexts": ["page"],
"onclick": sendCookieAndUA
})
4.content代碼
(function() {
document.addEventListener('DOMContentLoaded', function () {
var div = document.createElement('div')
div.id = 'cookie-block'
div.style.display = 'none'
document.body.appendChild(div);
})
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
if (request !== 'ok') {
document.getElementById('cookie-block').innerText = JSON.stringify(request)
sendResponse('ok')
}
}
)
})();
window.addEventListener('message', event => {
if (event.source !== window) {
return
}
// 如果是主頁面發(fā)送message, 則與background通信, 獲取頁面的 tabId
if (event.data && event.data.hasOwnProperty('type') && event.data.type === 'tab' && event.data.hasOwnProperty('level') && event.data.level === 'main') {
chrome.runtime.sendMessage({type: 'tab', level: 'main'}, function(response) {
console.log('收到響應', response)
})
}
}, false)
5.功能演示
1.右鍵復制當前頁Cookies
演示圖片
2.右鍵復制當前頁UserAgent
演示圖片
3.右鍵將Cookies和UserAgent發(fā)送到主頁面
主頁面需要先發(fā)送 message 給插件, 緩存頁面 tabId
window.parent.postMessage({type: 'tab', level: 'main'}, '*');
然后在要獲取Cookie與UserAgent的頁面右鍵選擇"發(fā)送Cookies和UA到主頁面"
演示圖片
6.參考資料
- https://zhuanlan.zhihu.com/p/57820028
- https://github.com/sxei/chrome-plugin-demo
- https://developer.chrome.com/extensions/api_index
- 通信相關: https://developer.chrome.com/extensions/messaging
- 內容JS: https://developer.chrome.com/extensions/content_scripts
- 如何調試popup: 右鍵點擊審查元素
https://stackoverflow.com/questions/5039875/debug-popup-html-of-a-chrome-extension - 如何調試background: 進入chrome://extensions/, 開啟開發(fā)者模式, 點擊查看視圖后面的按鈕
- popup按鈕添加點擊事件https://stackoverflow.com/questions/13591983/onclick-within-chrome-extension-not-working