前言
今年我們團隊打算在 Chrome 瀏覽器推出一款「網頁內容檢核」的擴充功能,主要目的是增加產品曝光機會,同時也能為使用者提供實用的工具。
這篇文章+將完整分享:
- Chrome Extension 開發重點 - 架構設計與必備知識
- 整合 Keycloak 登入 - 如何在擴充功能中實現 SSO 單一登入
- 產品導流策略 - 如何將免費工具轉換為付費客戶
- 上架 Chrome 商店 - 從打包到審核的完整流程
產品定位與目標用戶
這款擴充功能的主要目標用戶是行銷小編與社群經營者。他們在發文前,常常需要確認內容是否符合相關法規要求,避免廣告文案觸犯法規而遭到處罰。
使用情境
想像一下這個場景:
小編準備在 Facebook 發布一則保健食品的促銷貼文,在正式發布前,她想確認文案中是否有違規用詞。這時候,她只需要選取文字,右鍵點擊,就能快速檢查內容是否合規。

核心功能設計
右鍵選單功能
使用者在網頁上選取文字後,可透過右鍵選單快速進行檢查:
四種檢查模式
- 📝 檢查選取文字 - 直接檢查選取的文字內容
- 🔗 檢查連結內容 - 抓取網頁內容進行分析
- 🖼️ 檢查圖片文字 - 透過 OCR 識別圖片中的文字
- ✂️ 截圖檢查 - 截取畫面區域進行文字識別與檢查
技術架構
這個專案採用 Manifest V3 架構開發,主要技術組成如下:
chrome-extension/
├── manifest.json # 插件配置檔(Manifest V3)
├── background.js # Service Worker
├── content.js # 內容腳本
├── config.js # 集中設定檔
│
├── auth/ # 認證模組(Keycloak 整合)
│ ├── auth-config.js # 認證設定
│ ├── auth-service.js # 登入邏輯
│ ├── auth.html # 登入頁面
│ └── auth.js # 登入 UI 控制
│
├── core/ # 核心模組
│ ├── utils.js # 共用工具
│ ├── risk-levels.js # 風險等級定義
│ └── api-client.js # API 呼叫封裝
│
├── handlers/ # 功能處理器
│ ├── text-handler.js # 文字檢查
│ ├── link-handler.js # 連結檢查
│ ├── image-handler.js # 圖片 OCR
│ └── screenshot-handler.js # 截圖檢查
│
├── ui/ # UI 元件
│ ├── loading.js # 載入動畫
│ ├── dialog.js # 對話框
│ ├── result-panel.js # 結果面板
│ └── styles.css # 樣式
│
├── popup/ # Popup 頁面
│ ├── popup.html
│ ├── popup.css
│ └── popup.js
│
└── options/ # 設定頁面
├── options.html
├── options.css
└── options.js
Chrome Extension 開發重點
如果你是第一次開發 Chrome 擴充功能,以下是幾個必須了解的核心概念:
什麼是 Chrome Extension?
簡單來說,Chrome Extension 就是一個跑在瀏覽器裡的小程式,可以:
- 在網頁上加入新功能(如右鍵選單、浮動按鈕)
- 讀取或修改網頁內容
- 與外部 API 溝通
- 儲存使用者設定
Manifest V3 vs V2:一定要用新版!
Chrome 已經強制要求新的擴充功能使用 Manifest V3,2024 年起 V2 將逐步淘汰。
| 差異 | Manifest V2(舊版) | Manifest V3(新版) |
|---|---|---|
| 背景執行 | Background Page(常駐) | Service Worker(按需啟動) |
| 權限管理 | 較寬鬆 | 更嚴格,需明確宣告 |
| 安全性 | 一般 | 更高,限制 remote code |
| 上架 | 已停止接受 | 必須使用 |
五大核心檔案
開發 Chrome Extension 一定會用到這些檔案:
manifest.json 設定範例
這是擴充功能最重要的檔案,告訴 Chrome 這個擴充功能需要什麼權限:
{
"manifest_version": 3,
"name": "快合規快篩",
"version": "1.1.0",
"description": "圈選任何文字,快速檢查是否符合廣告法規",
"permissions": [
"contextMenus", // 右鍵選單
"activeTab", // 存取當前分頁
"storage", // 儲存資料
"notifications", // 顯示通知
"scripting", // 動態注入腳本
"identity" // OAuth 登入
],
"host_permissions": [
"https://api.textcomply.com/*", // API 網址
"https://sso.example.com/*" // SSO 登入網址
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup/popup.html",
"default_icon": "logo/icon128.png"
}
}
各元件如何溝通?
Chrome Extension 的各個部分是「隔離」的,需要透過 Message Passing 溝通:
程式碼範例:
// content.js - 發送訊息給 background
chrome.runtime.sendMessage(
{ action: 'checkText', text: '選取的文字內容' },
(response) => {
console.log('檢查結果:', response);
}
);
// background.js - 接收並處理訊息
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'checkText') {
// 呼叫 API 檢查文字
checkTextApi(message.text).then(result => {
sendResponse(result);
});
return true; // 表示會非同步回應
}
});
整合 Keycloak SSO 登入
如果你的產品需要使用者登入,可以整合 Keycloak(或其他 OAuth2 / OIDC 提供者)來實現單一登入。

為什麼選 Keycloak?
- ✅ 開源免費 - 不用付授權費
- ✅ 支援多種登入方式 - 帳密、Google、GitHub、Facebook...
- ✅ 標準協定 - 使用 OAuth2 / OpenID Connect
- ✅ 企業級功能 - 角色管理、多租戶、MFA 等
整合流程概覽
設定 Keycloak Client
在 Keycloak 管理後台建立 Client:
Client ID: chrome-extension
Client Protocol: openid-connect
Access Type: public (擴充功能無法保密 client_secret)
Valid Redirect URIs: https://<extension-id>.chromiumapp.org/*
Web Origins: *
PKCE 安全機制(重要!)
因為 Chrome Extension 是「公開客戶端」(無法安全儲存 client_secret),必須使用 PKCE 來增加安全性:
// 1. 產生隨機的 code_verifier
function generateRandomString(length = 64) {
const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
const array = new Uint8Array(length);
crypto.getRandomValues(array);
return Array.from(array, (byte) => charset[byte % charset.length]).join('');
}
// 2. 用 code_verifier 產生 code_challenge
async function generateCodeChallenge(codeVerifier) {
const encoder = new TextEncoder();
const data = encoder.encode(codeVerifier);
const hash = await crypto.subtle.digest('SHA-256', data);
// Base64 URL 編碼
const bytes = new Uint8Array(hash);
let binary = '';
bytes.forEach(byte => binary += String.fromCharCode(byte));
return btoa(binary)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
發起登入請求
async function loginWithKeycloak() {
// 1. 產生 PKCE 參數
const codeVerifier = generateRandomString(64);
const codeChallenge = await generateCodeChallenge(codeVerifier);
const state = generateRandomString(32);
// 2. 儲存 verifier(稍後換 token 時需要)
await chrome.storage.local.set({ codeVerifier, state });
// 3. 組合登入 URL
const authUrl = new URL('https://sso.example.com/realms/myrealm/protocol/openid-connect/auth');
authUrl.searchParams.set('client_id', 'chrome-extension');
authUrl.searchParams.set('redirect_uri', chrome.identity.getRedirectURL());
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'openid profile email');
authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
authUrl.searchParams.set('state', state);
// 4. 開啟登入視窗
chrome.identity.launchWebAuthFlow(
{ url: authUrl.toString(), interactive: true },
handleAuthCallback
);
}
換取 Access Token
async function exchangeCodeForToken(code) {
const { codeVerifier } = await chrome.storage.local.get('codeVerifier');
const response = await fetch(
'https://sso.example.com/realms/myrealm/protocol/openid-connect/token',
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: 'chrome-extension',
code: code,
redirect_uri: chrome.identity.getRedirectURL(),
code_verifier: codeVerifier // PKCE 驗證
})
}
);
const tokens = await response.json();
// 儲存 token
await chrome.storage.local.set({
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
expiresAt: Date.now() + (tokens.expires_in * 1000)
});
return tokens;
}
Token 自動刷新
Access Token 通常幾分鐘就會過期,需要自動刷新:
async function getValidToken() {
const { accessToken, refreshToken, expiresAt } =
await chrome.storage.local.get(['accessToken', 'refreshToken', 'expiresAt']);
// Token 還沒過期,直接使用
if (expiresAt && Date.now() < expiresAt - 60000) {
return accessToken;
}
// Token 即將過期,用 refresh_token 換新的
if (refreshToken) {
const newTokens = await refreshAccessToken(refreshToken);
return newTokens.access_token;
}
// 沒有 token,需要重新登入
return null;
}
導流策略設計
這才是這個專案最精華的部分 - 如何將免費工具的使用者轉換為潛在客戶。
漏斗設計流程
等待時間的價值
當使用者發起檢查請求後,因為需要等待 AI 模型回應(通常 3-5 秒),這段時間就是絕佳的廣告曝光時機!
// config.js - 輪播設定
CAROUSEL_IMAGES: [
'images/banner_square-1.png',
'images/banner_square-2.png'
],
CAROUSEL_INTERVAL_MS: 3000,
我們在 Loading 畫面中嵌入圖片輪播,投放產品廣告或促銷訊息。
結果頁面的導流設計
檢查結果會顯示:
- ✅ 是否合規
- ⚠️ 違規項目數量
- 🔒 部分資訊以模糊方式呈現
關鍵設計:違規說明和調整建議會顯示前 15 個字,其餘以「...」遮蔽,引導使用者點擊解鎖。
// 說明(違規原因)- 鎖定狀態
if (violation.explanations && violation.explanations.length > 0) {
const expPreview = escapeHtml(violation.explanations[0]).substring(0, 15) + '...';
html += `
<a href="${ctaLink}" target="_blank" class="compliance-result__locked-block">
<div class="compliance-result__locked-header">
<p class="compliance-result__locked-label">📋 違規說明</p>
<span class="compliance-result__locked-icon">🔒</span>
</div>
<p class="compliance-result__locked-text compliance-result__locked-text--blur">${expPreview}</p>
<p class="compliance-result__locked-cta">立即體驗完整功能</p>
</a>
`;
}
CTA 按鈕設計
在結果面板底部,我們放置了醒目的行動呼籲按鈕:
// CTA 按鈕
html += `
<div class="compliance-result__footer">
<a href="${ctaLink}" target="_blank" class="compliance-result__cta-btn">
🚀 使用產品取得完整建議
</a>
</div>
`;
Lead Generation 機制
當使用者點擊「取得完整建議」按鈕,會被導向解鎖頁面:
解鎖流程
- 使用者填寫 Email
- 驗證 Email 有效性
- 解鎖完整報告
- 每組 Email 只能解鎖一篇報告
這個設計的巧妙之處:
- ✅ 使用者獲得有價值的完整分析報告
- ✅ 我們獲得有效的潛在客戶名單
- ✅ 業務團隊可以後續進行接觸與轉化
安裝與使用
從 Chrome 線上應用程式商店安裝
使用者可以直接在 Chrome 線上應用程式商店搜尋並安裝這個擴充功能。
開發者模式安裝
如果你想自己部署類似的專案:
- 開啟 Chrome →
chrome://extensions/ - 開啟「開發人員模式」
- 點擊「載入未封裝項目」
- 選取
chrome-extension資料夾
使用方式
- 在任何網頁上選取文字
- 按下右鍵
- 選擇「🛡️ 廣告合規檢查」
- 選擇檢查類型
- 等待結果顯示
開發心得與建議
Manifest V3 的挑戰
Chrome 已經強制要求新的擴充功能使用 Manifest V3,這帶來了一些開發上的改變:
- Service Worker 取代了 Background Page
- 需要更明確地宣告權限
- Content Script 的注入方式有所改變
模組化設計
建議將功能拆分成獨立模組:
core/- 核心邏輯handlers/- 各種檢查處理器ui/- UI 元件
這樣的設計讓程式碼更易於維護和擴展。
API 設計考量
// 集中設定檔
const CONFIG = {
DEFAULT_API_ENDPOINT: 'https://your-api-endpoint.com',
IMAGE_OCR_PATH: '/api/image/ocr',
CREATE_AND_CHECK_PATH: '/api/TextContent/CreateAndCheck',
MAX_RETRIES: 3,
RETRY_DELAY: 1000,
};
建議將 API 端點設計為可配置,方便未來切換環境或更新服務。
成效預期
目前擴充功能尚未正式上架,以下為規劃階段的初期目標,待上架後將依實際數據持續調整:
| 指標 | 初期目標 |
|---|---|
| 擴充功能安裝數 | 500+ 次(上架後追蹤) |
| 每月活躍用戶 | 200 人以上 |
| Lead 轉換率 | 5–10% |
| 有效名單數 | 每月 10–50 筆 |
總結
Chrome Extension 不只是一個工具,更是一個絕佳的產品導流渠道。透過提供免費且實用的功能,我們能夠:
- 建立品牌認知 - 使用者每天使用,自然記住產品
- 收集潛在客戶 - 透過解鎖機制獲取有效名單
- 降低獲客成本 - 相較於廣告投放,擴充功能的 CAC 更低
- 提供價值交換 - 使用者獲得工具,我們獲得 Lead
如果你也在思考如何為產品創造新的曝光管道,Chrome Extension 絕對值得一試!




























留言