chrome作为目前最流行的浏览器,备受前端推崇,原因除了其对于前端标准的支持这一大核心原因之外,还有就是其强大的扩展性。如今基于其内核开发的插件已经非常多,给我们的生活带来了非常大的方便。
这里我们来讲一下其便利的插件开发
前置知识
chrome插件开发非常简单,只需要chrome浏览器
和一个编辑器
,只要会js
就能写一个基本的插件。
插件目录结构
manifest.json
必须,插件的配置文件icon.png
19*19的半透明png图片popup.html
弹出页面popup.js
弹出页面的js
manifest配置
这是一个chrome插件最重要也是必不可少的文件,它就是插件的门户,用来配置和插件相关的所有配置,其中manifest_version
、name
、version
是必不可少的,description
和icons
是推荐的
{
// 配置文件的版本,指定为2
"manifest_version": 2,
// 插件名称
"name": "plugin-name",
// 插件版本
"version": "1.0.0",
// 插件描述
"description": "这是插件描述",
// 插件图标
"icons":
{
"16": "img/icon.png",
"48": "img/icon.png",
"128": "img/icon.png"
},
// 常驻后台JS或页面
"background":
{
// 2种指定方式,如果指定JS,那么会自动生成一个背景页
"page": "background.html"
//"scripts": ["js/background.js"]
},
// 浏览器右上角图标设置,browser_action、page_action、app必须三选一
"browser_action":
{
"default_icon": "img/icon.png",
// 图标悬停时的标题,可选
"default_title": "这是一个插件demo",
"default_popup": "popup.html"
},
// 当某些特定页面打开才显示的图标
/*"page_action":
{
"default_icon": "img/icon.png",
"default_title": "我是pageAction",
"default_popup": "popup.html"
},*/
// 需要直接注入页面的JS
"content_scripts":
[
{
// "<all_urls>" 表示匹配所有地址
"matches": ["<all_urls>"],
// 注入js脚本,多个JS按顺序注入
"js": ["js/jquery-1.8.3.js", "js/content-script.js"],
// 注入css样式
"css": ["css/custom.css"],
// 注入时间,可选值: "document_start", "document_end", or "document_idle",最后一个表示页面空闲时,默认document_idle
"run_at": "document_start"
}
],
// 权限申请
"permissions":
[
"storage", // 插件本地存储
],
// 普通页面能够直接访问的插件资源列表,如果不设置是无法直接访问的
"web_accessible_resources": ["js/inject.js"],
// 插件主页
"homepage_url": "https://www.baidu.com",
// 覆盖浏览器默认页面
"chrome_url_overrides":
{
// 覆盖浏览器默认的新标签页
"newtab": "newtab.html"
},
// Chrome40以前的插件配置页写法
"options_page": "options.html",
// Chrome40以后的插件配置页写法,如果2个都写,新版Chrome只认后面这一个
"options_ui":
{
"page": "options.html",
// 添加一些默认的样式,推荐使用
"chrome_style": true
},
// 向地址栏注册一个关键字以提供搜索建议,只能设置一个关键字
"omnibox": { "keyword" : "go" },
// 默认语言
"default_locale": "zh_CN",
// devtools页面入口,注意只能指向一个HTML文件,不能是JS文件
"devtools_page": "devtools.html"
}
开发
插件配置
下面是我的插件配置,既manifest.json
{
"manifest_version": 2,
"name": "aliyun-login",
"version": "1.0.2",
"description": "这是一个免密登陆阿里云的插件,需要配置首展运维平台一起使用",
"icons": {
"16": "img/icon.png",
"48": "img/icon.png",
"128": "img/icon.png"
},
"permissions": [
"storage"
],
"content_scripts": [
{
"matches": [
"http://127.0.0.1:8888/*",
"http://116.62.128.66:84/*",
"http://sa.fshows.com/*",
"https://signin.aliyun.com/*"
],
"js": ["cryptojs/aes.js", "cryptojs/pad-nopadding.js", "cryptojs/mode-ecb.js", "js/content-script.js"],
"run_at": "document_end"
}
]
}
插件实现
我的需求是:在我的某个页面A
并传递阿里云账号密码,拿到账号密码后保存至storage
,在阿里云的登陆页面B
检测storage
是否存在密码,存在则登陆
具体实现过程如下
- 1、运维平台页面——插件处理脚本。插件执行的时间:
页面加载完成
,此时,监听页面消息。监听到消息后,判断消息类型,接收账号密码,调用谷歌的api存储账号密码。
let hiddenElement = document.createElement('input')
hiddenElement.type = 'hidden'
hiddenElement.id = 'ALILOGINCHECKING'
// 向页面插入标签
document.body.append(hiddenElement)
// 监听运维平台某页面传递过来的账号密码
window.addEventListener('message', event => {
const { type, linkurl, username, password, secretKey } = event.data
if (type && type == 'FROM_CMDB_PAGE' && linkurl && chrome.storage) {
// 解密
const user = DecryptEcb(username, secretKey)
const pwd = DecryptEcb(password, secretKey)
chrome.storage.local.set({ userinfo: { username: user, password: pwd } })
window.open(linkurl, '_blank')
}
})
- 2、阿里云登陆页面——插件处理脚本。接收账号密码,js利用DOM完成登陆
chrome.storage.local.get('userinfo', object => {
// 取账号密码
const USERNAME = object.userinfo ? object.userinfo.username : ''
const PASSWORD = object.userinfo ? object.userinfo.password : ''
if (USERNAME && PASSWORD) {
// 清除密文
chrome.storage.local.clear(() => {
// ...具体的登陆
})
}
})
- 3、阿里云的子账户号登陆需要
先输入账号
->点击下一步
->输入密码
->登陆
以下是我的第一次尝试
// 输入用户名
document.getElementById('user_principal_name').value = USERNAME
// 执行下一步
document.querySelector('.step-main.step-1').setAttribute('class', 'step-main step-2')
document.querySelector('.text.form-buttons').click()
// 输入密码
document.getElementById('password_ims').value = PASSWORD
// 登陆
document.querySelectorAll('.step-main.step-2')[1].setAttribute('class', 'step-main step-2')
document.querySelector('.step-2 .fm-button.submit-btn').click()
上述脚本执行后并未完成登陆,而是到了最后一步停止了
猜想可能是阿里的点击按钮做了什么操作,最后用上了延迟大法
setTimeout(() => {
document.querySelector('.step-2 .fm-button.submit-btn').click()
}, 60)
然而,这种方式虽然满足了需求,能够登陆。但是偶尔还是会发送无法登陆的情况,这样只能将延迟时间调大,这种方法我并不能接受。思考良久,骤然发现,我完全可以绕开阿里云的限制,否管它有什么逻辑,我直接提交表达不就ok,下面是我改进后的方案:
document.getElementById('user_principal_name').value = USERNAME // 输入用户名
document.getElementById('password_ims').value = PASSWORD // 输入密码
document.querySelector('.login-form').submit() // 登陆
代码很简洁明了,也实现了需求,目前暂未发现问题。
- 4、运维平台的某个页面点击链接跳转阿里云——
拦截处理
。这里我是通过判断浏览器的window.navigator.vendor
是否包含Google
来判断浏览器的内核,并检测页面上是否生成了id
为ALILOGINCHECKING
的标签,满足条件则向脚本处发送明文账号密码
// 判断是否为谷歌浏览器,并且安装了插件
const url = '待跳转的阿里云链接'
if (window.navigator.vendor.indexOf('Google') > -1 && document.getElementById('ALILOGINCHECKING')) {
// 掉接口获取账号密码
GET_ALI_ACCOUNT().then(res => {
// 向插件发账号密码,免密登陆阿里云
window.postMessage({ type: "FROM_CMDB_PAGE", linkurl: url, username: res.username, password: res.password, secretKey: 'ghgerEXZYaaUj8xz' }, "*")
})
} else {
window.open(url, '_blank')
}
- 5、
AES
的ECB
加密解密。了解更多请参考JS AES加密解密(ECB/CBC)
// 定义全局的解密方法
const DecryptEcb = (keyword, secretKey) => {
try {
let key = CryptoJS.enc.Utf8.parse(secretKey)
let encryptedHexStr = CryptoJS.enc.Hex.parse(keyword)
let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr)
let decrypt = CryptoJS.AES.decrypt(srcs, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.NoPadding })
let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8)
return decryptedStr.toString()
} catch (error) {
console.log(error)
return ''
}
}
插件的安装、调试、打包
从chrome浏览器的右上角菜单
->更多工具
->扩展程序
可以进入插件管理
页面,也可以直接在地址栏输入chrome://exensions 访问,勾选开发者模式
,选择加载以解压的扩展程序
完成安装。
开发中,代码有任何改动都必须重新加载插件,只需要在插件管理页按下Ctrl+R即可,以防万一最好还把页面刷新一下
打包,同样在此页面选择打包扩展程序
即可
疑难点
1、两个脚本页面如何共享数据
浏览器的API提供了storage用来存储信息
chrome.storage.local.set({ userinfo: { username: user, password: pwd } })
chrome.storage.local.get('userinfo', object => {})
chrome.storage.local.clear(() => {})
2、后台页面与插件页面如何交互
// 接收
window.addEventListener('message', event => {})
// 发送
window.postMessage({ type: "FROM_CMDB_PAGE" }, "*")
3、判断插件状态
未找到浏览器自带的安装以及卸载生命周期,因此无法判断是否安装了插件。利用插件向页面插入一特定标签,页面检测是否存在此标签来解决
4、服务器下载插件无权限
插件打包成功后放置服务器,前端需要配合a标签完成下载安装,但是此时发现链接报403
解决方法: chmod 777 aliyun_login.crx
总结
每天进步一点点,积少成多,水滴石穿!