chrome插件开发 | 掘金签到助手

前言

碰巧掘金新上签到活动,碰巧喝了咖啡睡不着,碰巧双休不用上班,所以写了个插件,方便工作日签到(顺道练练手)

先来看看成品

image-20210717064346915
image-20210717103555717
image-20210717064223932

总的来看,插件需要实现以下几个目标:

  1. 检测当前用户登录态
  2. 判断用户今天是否已签到
  3. 发送签到请求
  4. 展示信息(用户信息,奖励信息)
搞事情、搞事情、整天就知道搞事情!(熊猫人)

请求分析

用户登录凭证

通过登录请求[POST] /passport/web/user/login可以看到,请求响应设置的Cookie中有好几个键值对,选择一个普通的请求在postman上分析,可以看到掘金通过Cookie中的sessionid作为用户登录凭证

image-20210717143831542

签到相关的接口

在签到功能的请求中,跟这次要实现功能相关的接口有4个,分别是:

  1. 获取签到天数的汇总信息 [GET]/growth_api/v1/get_counts
  2. 获取当前的矿石数量 [GET]/growth_api/v1/get_cur_point
  3. 判断用户今天是否已签到 [GET]/growth_api/v1/get_today_status
  4. 用户签到 [POST]/growth_api/v1/check_in

这里大致分析一下功能对应的请求即可,具体传参以及返回值的含义可以通过浏览器控制台查看(F12

流程图

下面通过几个场景的时序图来阐述清楚插件的工作流程

未登录场景

image.png

未签到场景

image.png

已签到场景

image.png

搭建chrome插件开发工程

通过vue-web-extension实现快速搭建chrome插件开发工程(Vue)

首先确保这两个已经安装了

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

然后通过vue-web-extension创建工程,我选择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的配置会写在package.json文件中babel部分,跟工程原有的.babelrc配置文件重叠了,需要将package.json中关于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配置文件如下

在工程根目录下执行yarn build,能够正常打包

yarn build

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

至此,工程已经基本搭建完成了!可以正式投入开发

工程常用命令:

  • yarn build 构建插件,输出到dist目录下
  • yarn build-zip 按照插件名+版本号的形式,构建插件压缩包
  • yarn watch 构建插件,输出到dist目录下,如果发生改动,会即时刷新

关键代码

manifest.json配置文件

manifest.json文件中记载着插件的原信息,其中包括插件的基础信息(插件名称,版本号,ICON等),以及插件涉及页面(popup,options,background等),还有插件需要向chrome申请的权限

{
  // 插件名称
  "name": "juejin-auto-sign",
  // 插件描述
  "description": "掘金签到助手",
  // 插件版本号
  "version": "1.0.0",
  "manifest_version": 2,
  "icons": {
    "48": "icons/icon.png",
    "128": "icons/icon.png"
  },
  ......
  // [1] 申请掘金的cookie、网络请求的权限
  "permissions": [
    "cookies",
    "*://*.juejin.cn/",
    "webRequest",
    "webRequestBlocking"
  ]
}

[1]处可以看到,插件需要申请网络权限 webRequestwebRequestBlocking,这两个权限是跟用户签到请求有关系的(POST请求),后面会详细介绍为什么需要这两个权限

popup页面

目前插件的功能实现都是在popup页面,所谓popup页面就是在浏览器插件栏处点击展示的页面

image-20210717224513506

Google翻译插件来看,红色箭头指向的页面就是popup页面

chrome插件开发有分好几种页面以及脚本
页面有: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">
      当前矿石数量:<el-tag size="mini" type="success">{{ currentPoint }}</el-tag>
    </div>
    <div class="sign-label">
      连续签到天数:<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',
      // 当前矿石数量
      currentPoint: 0,
      // 连续签到天数
      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;
    // 矿石数量
    resp = await getCurrentPoint();
    this.currentPoint = resp.data.data;
    // 连续签到天数
    resp = await getSignData();
    this.continueSignDays = resp.data.data.cont_count;
    // 当前签到状况
    resp = await getTodaySign();
    this.todaySign = resp.data.data;
    this.loading = false;
  },
};
</script>

主要的逻辑都包含在页面mounted阶段,该阶段需要执行一系列操作,包括获取用户信息,判断cookie有效性,获取用户当前签到状态以及奖励信息等等

忽略<template>中的一堆笨拙的<div>标签,前端我只会写<div>

img

修改请求头

签到请求/growth_api/v1/check_in是一个POST请求,浏览器会自动带上origin请求头,其值为chrome-extension://xxxxx,此时掘金会校验请求头中的origin,非掘金的origin会直接报403(应该是网关层做了请求来源的校验)

image-20210717230835866

此时插件就需要修改请求头中的origin字段,然而origin字段并不能随意修改

比方说axios强制指定请求头中origin的值是不生效的,并且插件的控制台会有对应的错误提示,该操作不符合规范

此时就需要使用到manifest.json中注册的网络请求的权限

一个正常响应的请求在chrome中会经历如下图所示的声明周期

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

如果我们需要修改请求头字段的话,则可以在onBeforeSendHeaders发送请求头之前通过事件监听的方式修改origin字段,同时,该事件监听应该是贯穿整个插件的生命周期的,所以代码应该写在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接受三个参数:

  1. 监听器回调方法,修改请求头的操作应该放在这个方法中
  2. 过滤器,用于控制监听的URL范围,这里选择监听掘金相关的请求
  3. 元信息(opt_extraInfoSpec),简单来说就是,这个参数填写的值代表监听器的回调方法的执行方式以及回调时传入的数据
    本次监听器的元数据中包含['blocking', 'requestHeaders', 'extraHeaders'],这三个值分别表示:
    • blocking 表示回调方法是同步调用的,就是说一个请求的回调方法执行完之后才会轮到下一个请求的回调方法
    • requestHeaders 表示回调方法的参数details 中包含请求头的数据
    • extraHeaders 这个字段比较神奇,由于origin请求头这玩意不是说改就改的,chrome也不推荐,如果真的要修改的话,就必须要填写这个字段,这样对origin的修改才会生效

在manifest配置文件中可以看到,插件申请的权限除了webRequest之外,还有webRequestBlocking,添加这个权限是因为监听方法中使用blocking同步的方式

总结

至此,这个插件的实现思路基本介绍完了,总的来看,插件的实现难度不高,有兴趣的可以自己试着尝试实现一下

当然,以插件的形式来实现签到的功能其实并没有极大幅度地提高签到的效率,最好的方式肯定还是在服务器中定时签到,这样即使不上掘金网站,也能收获满满的矿石用以抽奖,但是这样不可避免地将用户登录凭证暴露出去,可能存在一些安全风险,同时也丧失了掘金举办这个活动的意义

万一被别人拿到自己的cookie跑去删除自己的文章,那真的是欲哭无泪

这次掘金的签到活动个人觉得还是办的可以的

  • 任务难度很低,规则简单,活动入口明显

  • 签到奖励比较多,一个月签到下来,应该都有好几千矿石,应该能抽奖好几十次

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容