工作上遇到一个小问题, 就是桌面软件里有个打开浏览器获取cookie的功能, 这个功能C#里可能就是打开一个webview, 然后通过api获取页面cookie. 但是在网页端就很坑了, 放iframe也不行, 毕竟打开的页面不是可控的, 无法通信, 存在跨域问题.
实现代码: https://github.com/klren0312/cookies-chrome-plugin/edit/master/README.md
原理
如果非要获取, 只能用浏览器插件或者套个Electron, 当然还是用浏览器插件啦.浏览器插件, 通过右键点击发送, 可以将获取的cookie和ua发送到需要的页面.
首先插件会在每个页面创建一个id为'content-block'的DOM
, 然后主页面会通过postMessage
, 通知插件获取主页面的tabId
, 随后, 进入需要获取cookie
和ua
的页面, 右键获取, 然后通过之前缓存的主页面tabId
将获取的cookie
和ua
发送到content.js
, content.js
将cookie
和ua
组成的json
写入id
为'content-block'的DOM
, 主页面通过mutationObserver
监听id
为'content-block'的DOM
的变化, 触发数据获取
实现
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",
""
],
"icons": {
"16": "icon-16.png",
"48": "icon-48.png",
"128": "icon-128.png"
},
"background": {
"scripts": [
"utils.js"
]
},
"content_scripts": [{
"matches": [""],
"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
;最后是插件的应用域名, 这个如果想在所有域名下运用, 就使用
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)
}
// 发送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发送给主页面
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 缓存, 并发送给主页面
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": "发送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
}
// 如果是主页面发送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发送到主页面
主页面需要先发送 message 给插件, 缓存页面 tabId
window.parent.postMessage({type: 'tab', level: 'main'}, '*');
然后在要获取Cookie与UserAgent的页面右键选择"发送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/, 开启开发者模式, 点击查看视图后面的按钮
- popup按钮添加点击事件https://stackoverflow.com/questions/13591983/onclick-within-chrome-extension-not-working