chrome插件開發(fā) | 掘金簽到助手

前言

碰巧掘金新上簽到活動(dòng),碰巧喝了咖啡睡不著,碰巧雙休不用上班,所以寫了個(gè)插件,方便工作日簽到(順道練練手)

先來看看成品

image-20210717064346915
image-20210717103555717
image-20210717064223932

總的來看,插件需要實(shí)現(xiàn)以下幾個(gè)目標(biāo):

  1. 檢測當(dāng)前用戶登錄態(tài)
  2. 判斷用戶今天是否已簽到
  3. 發(fā)送簽到請求
  4. 展示信息(用戶信息,獎(jiǎng)勵(lì)信息)
搞事情、搞事情、整天就知道搞事情?。ㄐ茇埲耍?/div>

請求分析

用戶登錄憑證

通過登錄請求[POST] /passport/web/user/login可以看到,請求響應(yīng)設(shè)置的Cookie中有好幾個(gè)鍵值對,選擇一個(gè)普通的請求在postman上分析,可以看到掘金通過Cookie中的sessionid作為用戶登錄憑證

image-20210717143831542

簽到相關(guān)的接口

在簽到功能的請求中,跟這次要實(shí)現(xiàn)功能相關(guān)的接口有4個(gè),分別是:

  1. 獲取簽到天數(shù)的匯總信息 [GET]/growth_api/v1/get_counts
  2. 獲取當(dāng)前的礦石數(shù)量 [GET]/growth_api/v1/get_cur_point
  3. 判斷用戶今天是否已簽到 [GET]/growth_api/v1/get_today_status
  4. 用戶簽到 [POST]/growth_api/v1/check_in

這里大致分析一下功能對應(yīng)的請求即可,具體傳參以及返回值的含義可以通過瀏覽器控制臺查看(F12

流程圖

下面通過幾個(gè)場景的時(shí)序圖來闡述清楚插件的工作流程

未登錄場景

image.png

未簽到場景

image.png

已簽到場景

image.png

搭建chrome插件開發(fā)工程

通過vue-web-extension實(shí)現(xiàn)快速搭建chrome插件開發(fā)工程(Vue)

首先確保這兩個(gè)已經(jīng)安裝了

npm install -g @vue/cli
npm install -g @vue/cli-init

然后通過vue-web-extension創(chuàng)建工程,我選擇vue-web-extension的版本是v1

vue init kocal/vue-web-extension#v1 juejin-auto-sign

按需選擇自己需要的功能(axios必選)

image-20210717152313855

安裝element ui(按需加載)

cd juejin-auto-sign && vue add element
image-20210717174346824

由于element ui的配置會(huì)寫在package.json文件中babel部分,跟工程原有的.babelrc配置文件重疊了,需要將package.json中關(guān)于babel部分的配置合并到.babelrc文件中

合并前

# .babelrc配置文件
{
  "plugins": [
    "@babel/plugin-proposal-optional-chaining"
  ],
  "presets": [
    ["@babel/preset-env", {
      "useBuiltIns": "usage",
      "corejs": 3,
      "targets": {
        // https://jamie.build/last-2-versions
        "browsers": ["> 0.25%", "not ie 11", "not op_mini all"]
      }
    }]
  ]
}

# package.json配置文件
{
  .......
  "babel": {
    "plugins": [
      [
        "component",
        {
          "libraryName": "element-ui",
          "styleLibraryName": "theme-chalk"
        }
      ]
    ]
  }
}

合并后,將package.jsonbabel部分刪除,.babelrc配置文件如下

在工程根目錄下執(zhí)行yarn build,能夠正常打包

yarn build

![image-20210717175823529](/Users/luoxiongjian/Library/Application Support/typora-user-images/image-20210717175823529.png)

至此,工程已經(jīng)基本搭建完成了!可以正式投入開發(fā)

工程常用命令:

  • yarn build 構(gòu)建插件,輸出到dist目錄下
  • yarn build-zip 按照插件名+版本號的形式,構(gòu)建插件壓縮包
  • yarn watch 構(gòu)建插件,輸出到dist目錄下,如果發(fā)生改動(dòng),會(huì)即時(shí)刷新

關(guān)鍵代碼

manifest.json配置文件

manifest.json文件中記載著插件的原信息,其中包括插件的基礎(chǔ)信息(插件名稱,版本號,ICON等),以及插件涉及頁面(popup,options,background等),還有插件需要向chrome申請的權(quán)限

{
  // 插件名稱
  "name": "juejin-auto-sign",
  // 插件描述
  "description": "掘金簽到助手",
  // 插件版本號
  "version": "1.0.0",
  "manifest_version": 2,
  "icons": {
    "48": "icons/icon.png",
    "128": "icons/icon.png"
  },
  ......
  // [1] 申請掘金的cookie、網(wǎng)絡(luò)請求的權(quán)限
  "permissions": [
    "cookies",
    "*://*.juejin.cn/",
    "webRequest",
    "webRequestBlocking"
  ]
}

[1]處可以看到,插件需要申請網(wǎng)絡(luò)權(quán)限 webRequestwebRequestBlocking,這兩個(gè)權(quán)限是跟用戶簽到請求有關(guān)系的(POST請求),后面會(huì)詳細(xì)介紹為什么需要這兩個(gè)權(quán)限

popup頁面

目前插件的功能實(shí)現(xiàn)都是在popup頁面,所謂popup頁面就是在瀏覽器插件欄處點(diǎn)擊展示的頁面

image-20210717224513506

Google翻譯插件來看,紅色箭頭指向的頁面就是popup頁面

chrome插件開發(fā)有分好幾種頁面以及腳本
頁面有:popup,optional,background,插件上不同頁面的展示位置是不同,用途也不同,目前只需要了解到popup頁面即可
腳本有:background.js,content script等,不同的腳本聲明周期也是不同的

下面展示簽到助手插件popup頁面的主要代碼

<template>
  <div class="sign-body">
    <div class="sign-image">
      <el-avatar size="large" :src="imageUrl"></el-avatar>
    </div>
    <div class="sign-text">{{ nickName }}</div>
    <div class="sign-label">
      當(dāng)前礦石數(shù)量:<el-tag size="mini" type="success">{{ currentPoint }}</el-tag>
    </div>
    <div class="sign-label">
      連續(xù)簽到天數(shù):<el-tag size="mini" type="success">{{ continueSignDays }}</el-tag>
    </div>
    <div class="sign-btn" v-if="!loading">
      <el-button v-if="!login" type="primary" @click="toLogin">去登錄</el-button>
      <el-button v-else type="primary" :loading="signing" :disabled="todaySign" @click="toSign">{{ todaySign ? '已簽到' : '去簽到' }}</el-button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 頭像
      imageUrl: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png',
      // 昵稱
      nickName: 'null',
      // 當(dāng)前礦石數(shù)量
      currentPoint: 0,
      // 連續(xù)簽到天數(shù)
      continueSignDays: 0,
      // 是否登錄
      login: false,
      // 今日是否已簽到
      todaySign: true,
      loading: true,
      signing: false,
    };
  },
  .......
  async mounted() {
    this.loading = true;
    // 獲取用戶信息
    let resp = await getUserInfo();
    // 判斷cookie有效性
    this.login = !resp.data.err_no && resp.data.data;
    if (!this.login) {
      this.loading = false;
      return;
    }
    // 頭像,昵稱
    this.imageUrl = resp.data.data.avatar_large;
    this.nickName = resp.data.data.user_name;
    // 礦石數(shù)量
    resp = await getCurrentPoint();
    this.currentPoint = resp.data.data;
    // 連續(xù)簽到天數(shù)
    resp = await getSignData();
    this.continueSignDays = resp.data.data.cont_count;
    // 當(dāng)前簽到狀況
    resp = await getTodaySign();
    this.todaySign = resp.data.data;
    this.loading = false;
  },
};
</script>

主要的邏輯都包含在頁面mounted階段,該階段需要執(zhí)行一系列操作,包括獲取用戶信息,判斷cookie有效性,獲取用戶當(dāng)前簽到狀態(tài)以及獎(jiǎng)勵(lì)信息等等

忽略<template>中的一堆笨拙的<div>標(biāo)簽,前端我只會(huì)寫<div>

img

修改請求頭

簽到請求/growth_api/v1/check_in是一個(gè)POST請求,瀏覽器會(huì)自動(dòng)帶上origin請求頭,其值為chrome-extension://xxxxx,此時(shí)掘金會(huì)校驗(yàn)請求頭中的origin,非掘金的origin會(huì)直接報(bào)403(應(yīng)該是網(wǎng)關(guān)層做了請求來源的校驗(yàn))

image-20210717230835866

此時(shí)插件就需要修改請求頭中的origin字段,然而origin字段并不能隨意修改

比方說axios強(qiáng)制指定請求頭中origin的值是不生效的,并且插件的控制臺會(huì)有對應(yīng)的錯(cuò)誤提示,該操作不符合規(guī)范

此時(shí)就需要使用到manifest.json中注冊的網(wǎng)絡(luò)請求的權(quán)限

一個(gè)正常響應(yīng)的請求在chrome中會(huì)經(jīng)歷如下圖所示的聲明周期

Life cycle of a web request from the perspective of the webrequest API

如果我們需要修改請求頭字段的話,則可以在onBeforeSendHeaders發(fā)送請求頭之前通過事件監(jiān)聽的方式修改origin字段,同時(shí),該事件監(jiān)聽?wèi)?yīng)該是貫穿整個(gè)插件的生命周期的,所以代碼應(yīng)該寫在background.js

// background.js文件
chrome.webRequest.onBeforeSendHeaders.addListener(
  function(details) {
    details.requestHeaders.push({ name: 'origin', value: 'https://juejin.cn' });
    return { requestHeaders: details.requestHeaders };
  },
  { urls: ['*://*.juejin.cn/*'] },
  ['blocking', 'requestHeaders', 'extraHeaders']
);

上述代碼中可以看到,chrome.webRequest.onBeforeSendHeaders.addListener接受三個(gè)參數(shù):

  1. 監(jiān)聽器回調(diào)方法,修改請求頭的操作應(yīng)該放在這個(gè)方法中
  2. 過濾器,用于控制監(jiān)聽的URL范圍,這里選擇監(jiān)聽掘金相關(guān)的請求
  3. 元信息(opt_extraInfoSpec),簡單來說就是,這個(gè)參數(shù)填寫的值代表監(jiān)聽器的回調(diào)方法的執(zhí)行方式以及回調(diào)時(shí)傳入的數(shù)據(jù)
    本次監(jiān)聽器的元數(shù)據(jù)中包含['blocking', 'requestHeaders', 'extraHeaders'],這三個(gè)值分別表示:
    • blocking 表示回調(diào)方法是同步調(diào)用的,就是說一個(gè)請求的回調(diào)方法執(zhí)行完之后才會(huì)輪到下一個(gè)請求的回調(diào)方法
    • requestHeaders 表示回調(diào)方法的參數(shù)details 中包含請求頭的數(shù)據(jù)
    • extraHeaders 這個(gè)字段比較神奇,由于origin請求頭這玩意不是說改就改的,chrome也不推薦,如果真的要修改的話,就必須要填寫這個(gè)字段,這樣對origin的修改才會(huì)生效

在manifest配置文件中可以看到,插件申請的權(quán)限除了webRequest之外,還有webRequestBlocking,添加這個(gè)權(quán)限是因?yàn)楸O(jiān)聽方法中使用blocking同步的方式

總結(jié)

至此,這個(gè)插件的實(shí)現(xiàn)思路基本介紹完了,總的來看,插件的實(shí)現(xiàn)難度不高,有興趣的可以自己試著嘗試實(shí)現(xiàn)一下

當(dāng)然,以插件的形式來實(shí)現(xiàn)簽到的功能其實(shí)并沒有極大幅度地提高簽到的效率,最好的方式肯定還是在服務(wù)器中定時(shí)簽到,這樣即使不上掘金網(wǎng)站,也能收獲滿滿的礦石用以抽獎(jiǎng),但是這樣不可避免地將用戶登錄憑證暴露出去,可能存在一些安全風(fēng)險(xiǎn),同時(shí)也喪失了掘金舉辦這個(gè)活動(dòng)的意義

萬一被別人拿到自己的cookie跑去刪除自己的文章,那真的是欲哭無淚

這次掘金的簽到活動(dòng)個(gè)人覺得還是辦的可以的

  • 任務(wù)難度很低,規(guī)則簡單,活動(dòng)入口明顯

  • 簽到獎(jiǎng)勵(lì)比較多,一個(gè)月簽到下來,應(yīng)該都有好幾千礦石,應(yīng)該能抽獎(jiǎng)好幾十次

唯一不好的地方就是我抽了好幾天都沒抽到switch,哈哈哈哈

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容