JavaScript 和 Cookies

JavaScript 和 Cookies

本文將說明 JavaScript 與 Cookies。

我們將會從 Cookie 的基礎、讀取與寫入、安全性到實用範例,全都一步步詳細說明。

YouTube Video

javascript-cookie.html
  1<!DOCTYPE html>
  2<html lang="en">
  3<head>
  4  <meta charset="UTF-8">
  5  <title>JavaScript &amp; HTML</title>
  6  <style>
  7    * {
  8        box-sizing: border-box;
  9    }
 10
 11    body {
 12        margin: 0;
 13        padding: 1em;
 14        padding-bottom: 10em;
 15        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
 16        background-color: #f7f9fc;
 17        color: #333;
 18        line-height: 1.6;
 19    }
 20
 21    .container {
 22        max-width: 800px;
 23        margin: 0 auto;
 24        padding: 1em;
 25        background-color: #ffffff;
 26        border: 1px solid #ccc;
 27        border-radius: 10px;
 28        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
 29    }
 30
 31    .container-flex {
 32        display: flex;
 33        flex-wrap: wrap;
 34        gap: 2em;
 35        max-width: 1000px;
 36        margin: 0 auto;
 37        padding: 1em;
 38        background-color: #ffffff;
 39        border: 1px solid #ccc;
 40        border-radius: 10px;
 41        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
 42    }
 43
 44    .left-column, .right-column {
 45        flex: 1 1 200px;
 46        min-width: 200px;
 47    }
 48
 49    h1, h2 {
 50        font-size: 1.2rem;
 51        color: #007bff;
 52        margin-top: 0.5em;
 53        margin-bottom: 0.5em;
 54        border-left: 5px solid #007bff;
 55        padding-left: 0.6em;
 56        background-color: #e9f2ff;
 57    }
 58
 59    button {
 60        display: block;
 61        margin: 1em auto;
 62        padding: 0.75em 1.5em;
 63        font-size: 1rem;
 64        background-color: #007bff;
 65        color: white;
 66        border: none;
 67        border-radius: 6px;
 68        cursor: pointer;
 69        transition: background-color 0.3s ease;
 70    }
 71
 72    button:hover {
 73        background-color: #0056b3;
 74    }
 75
 76    #output {
 77        margin-top: 1em;
 78        background-color: #1e1e1e;
 79        color: #0f0;
 80        padding: 1em;
 81        border-radius: 8px;
 82        min-height: 200px;
 83        font-family: Consolas, monospace;
 84        font-size: 0.95rem;
 85        overflow-y: auto;
 86        white-space: pre-wrap;
 87    }
 88
 89    .highlight {
 90        outline: 3px solid #ffc107; /* yellow border */
 91        background-color: #fff8e1;  /* soft yellow background */
 92        transition: background-color 0.3s ease, outline 0.3s ease;
 93    }
 94
 95    .active {
 96        background-color: #28a745; /* green background */
 97        color: #fff;
 98        box-shadow: 0 0 10px rgba(40, 167, 69, 0.5);
 99        transition: background-color 0.3s ease, box-shadow 0.3s ease;
100    }
101  </style>
102</head>
103<body>
104    <div class="container">
105        <h1>JavaScript Console</h1>
106        <button id="executeBtn">Execute</button>
107        <div id="output"></div>
108    </div>
109
110    <script>
111        // Override console.log to display messages in the #output element
112        (function () {
113            // Override console.log
114            const originalLog = console.log;
115            console.log = function (...args) {
116                originalLog.apply(console, args);
117                const message = document.createElement('div');
118                message.textContent = args.map(String).join(' ');
119                output.appendChild(message);
120            };
121
122            // Override console.error
123            const originalError = console.error;
124            console.error = function (...args) {
125                originalError.apply(console, args);
126                const message = document.createElement('div');
127                message.textContent = args.map(String).join(' ');
128                message.style.color = 'red'; // Color error messages red
129                output.appendChild(message);
130            };
131        })();
132
133        document.getElementById('executeBtn').addEventListener('click', () => {
134            // Prevent multiple loads
135            if (document.getElementById('externalScript')) return;
136
137            const script = document.createElement('script');
138            script.src = 'javascript-cookie.js';
139            script.id = 'externalScript';
140            //script.onload = () => console.log('javascript-cookie.js loaded and executed.');
141            //script.onerror = () => console.log('Failed to load javascript-cookie.js.');
142            document.body.appendChild(script);
143        });
144    </script>
145</body>
146</html>

JavaScript 和 Cookies

“Cookie”是指儲存在使用者瀏覽器內的小型資料。它們主要用於以下幾個目的:。

  • 用戶驗證(維持登入狀態)
  • 保存使用者設定(語言、主題等等)
  • 追蹤(瀏覽紀錄等等)

在 JavaScript 中,你可以透過 document.cookie 來讀取和寫入 Cookie。

建立(寫入)Cookie

可以用以下語法建立 Cookie:。

1document.cookie = "username=JohnDoe";
  • 這段程式碼會在瀏覽器中儲存一個名為 "username=JohnDoe" 的 Cookie。

建立有失效日期的 Cookie

Cookie 可以指定失效日期。如果沒有指定失效日期,則會被視為工作階段 Cookie,瀏覽器關閉時會被刪除。

1const date = new Date();
2date.setTime(date.getTime() + (7 * 24 * 60 * 60 * 1000)); // 7 days
3const cookieText = "username=JohnDoe; expires=" + date.toUTCString() + "; path=/; SameSite=None; Secure"
4document.cookie = cookieText;
5console.log(`Cookie Text  : ${cookieText}`);
6console.log(`Cookie Value : ${document.cookie}`);
  • expires 屬性指定 Cookie 的失效日期(UTC 格式)。
  • path 屬性指定此 Cookie 會被傳送的路徑。/ 代表整個網站。
  • 如果指定 SameSite=None,則即使是跨站請求也會發送 Cookie。然而,在這種情況下,您必須同時包含 Secure 屬性。
  • 指定 Secure 屬性會限制 Cookie 只在 HTTPS 通訊中傳送,提高安全性。
  • 通過參考 document.cookie,您可以以字串形式獲取目前頁面上可用的所有 Cookie
  • 使用 console.log,您可以檢查瀏覽器中實際設定的 Cookie 值與能夠取得的值之間的差異。

取得(讀取)Cookie

你可以透過 document.cookie 以字串方式取得所有 Cookie。

1const date = new Date();
2date.setTime(date.getTime() + (7 * 24 * 60 * 60 * 1000)); // 7 days
3document.cookie = "theme=dark; expires=" + date.toUTCString() + "; path=/; SameSite=None; Secure"
4
5console.log(document.cookie);
6// Output: "username=JohnDoe; theme=dark"
  • document.cookie 的返回值是一個單一字串,其中所有 Cookie 會以 key=value; 的格式串接起來。
  • 通常會用函式來解析這個字串,取得所需的值會更方便。

取得 Cookie 值的函式

 1function getCookie(name) {
 2    const cookies = document.cookie.split('; ');
 3    for (const cookie of cookies) {
 4        const [key, value] = cookie.split('=');
 5        if (key === name) {
 6            return decodeURIComponent(value);
 7        }
 8    }
 9    return null;
10}
11
12console.log(getCookie("username")); // "JohnDoe"
  • 這個函式使用 split() 分割鍵和值,並在匹配指定鍵時返回該值。
  • 通過使用 decodeURIComponent,您可以正確地取得經編碼的字元。
  • 如果沒有對應的鍵,則會返回 null

刪除 Cookie

將 Cookie 的失效日期設為過去的時間即可刪除該 Cookie。

1document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=None; Secure";
2
3console.log(document.cookie);
4// Output: "theme=dark"
  • username 賦值為空,並將過期日期設定為過去時間,該 Cookie 就會被刪除。
  • 刪除 Cookie 時,必須將 pathSameSiteSecure 屬性與建立時所用的一致。
  • 在這個範例中,username 已被刪除,其它如 theme=darkCookie 仍然保留。

Cookie 選項

你可以指定多種選項來控制 Cookie 的行為。主要選項如下:。

  • expires 指定 Cookie 的失效日期(UTC 格式)。如果未指定,則 Cookie 會變成工作階段 Cookie,瀏覽器關閉時刪除。

  • max-age 以秒數指定 Cookie 的有效期限。此屬性優先於 expires

  • path 指定 Cookie 傳送到哪些路徑。例如指定 /admin 時,Cookie 只會發送到該路徑下的頁面。

  • domain 指定 Cookie 有效的網域名稱。通常設為目前網域,也可以設為如 .example.com 這種所有子網域都適用。

  • secure 若設置此屬性,Cookie 僅會經過 HTTPS 傳送。為了提高安全性,儲存敏感資訊時請務必加上此屬性。

  • SameSite 用來控制跨站請求時是否傳送 Cookie。你可以指定以下三種值之一:。

    • Strict Cookie 僅會在同一網站的請求中傳送。

    • Lax 一般導覽會傳送 Cookie,但有部分限制。

    • None 即使是跨站請求,也會傳送 Cookie。但此時也必須加上 Secure 屬性。

範例:安全 Cookie

1document.cookie = "sessionId=abc123; secure; SameSite=Strict";
  • 如果指定 secureCookie 只會在 HTTPS 通訊中發送。
  • 如果指定 SameSite=StrictCookie 在跨站請求時不會被送出,可作為 CSRF 的防護措施。
  • 這類安全屬性對於用於認證或會話管理的重要 Cookie 是必不可少的。

編碼與解碼

因為 Cookie 的值可能包含特殊字元,所以建議用 encodeURIComponent 來處理會更安全。

1const date = new Date();
2date.setTime(date.getTime() + (7 * 24 * 60 * 60 * 1000)); // 7 days
3
4const username = "JohnDoe";
5document.cookie =
6    "username=" + encodeURIComponent(username) +
7    "; max-age=604800; path=/; SameSite=None; Secure";
8
9console.log(decodeURIComponent(getCookie("username"))); // "JohnDoe"
  • 透過使用 encodeURIComponent,可以安全地在 Cookie 中儲存空格、符號等內容。
  • 讀取時,請使用 decodeURIComponent 還原為原始字串。
  • 在這個範例中,使用 max-age=604800 將過期期限設為7天(604,800秒)。這是指定過期日期的方法,與 expires 類似。max-age 可以用秒來指定,通常更方便使用。

實用範例:保存與載入主題

以下是將用戶選擇的主題存入 Cookie,並於下次自動套用的範例。

 1function setTheme(theme) {
 2    document.cookie =
 3        "theme=" + encodeURIComponent(theme) +
 4        "; max-age=604800; path=/; SameSite=None; Secure"; // 1 week
 5    applyTheme(theme);
 6}
 7
 8function applyTheme(theme) {
 9    document.body.style.backgroundColor = theme === "dark" ? "#333" : "#fff";
10    document.body.style.color = theme === "dark" ? "#fff" : "#000";
11}
12
13function getCookie(name) {
14    const cookies = document.cookie.split('; ');
15    for (const cookie of cookies) {
16        const [key, value] = cookie.split('=');
17        if (key === name) {
18            return decodeURIComponent(value);
19        }
20    }
21    return null;
22}
23
24const savedTheme = getCookie("theme");
25if (savedTheme) {
26    applyTheme(savedTheme);
27}
1<button onclick="setTheme('light')">Light</button>
2<button onclick="setTheme('dark')">Dark</button>
  • setTheme 函式會把選擇的主題儲存到 Cookie,並立即呼叫 applyTheme 來更新畫面。
  • applyTheme 函式根據主題切換 body 的背景顏色和文字顏色。
  • 在這個例子中,由於設置了 max-age=604800,主題設定會保留一週。
  • 由於使用者的選擇在重新訪問頁面時能被保留,因此提升了使用者體驗(UX)。

Cookie 限制與注意事項

使用 Cookie 時請注意以下事項:。

  • 容量限制 單一 Cookie 的容量限制約為 4KB。

  • 可儲存的 Cookie 數量限制 根據瀏覽器的不同,每個網域大約只能儲存20到50條 Cookie。

  • 安全注意事項 Cookie 的內容基本上是以明文儲存,因此不適合用來存放密碼等機密資訊。

  • JavaScript 無法存取的 Cookie 帶有 HttpOnly 屬性的 Cookie,為了安全理由,無法從 JavaScript 讀取。

伺服器與 Cookie

1Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict

某些 Cookie 屬性,例如 HttpOnly,無法透過 JavaScript 設定。這些必須在伺服器端設置。

總結

使用 JavaScript 操作 Cookie 可以提升使用者體驗並維持狀態。但請記得以下幾點,以安全並正確地處理。

  • 只儲存最必要的資訊 從隱私與安全的角度來看,應避免儲存個人或敏感資訊,只紀錄必要資料。

  • 正確設置安全性屬性 設定 SecureSameSite 等屬性,以防止 XSS 或 CSRF 等跨站攻擊。

  • 資料的編碼與解碼 請使用 encodeURIComponentdecodeURIComponent 以安全地儲存和讀取 Cookie 值,以正確處理特殊符號及日文。

學習正確使用 Cookie,可以打造更進階、更加安全的網頁應用程式。

您可以在我們的 YouTube 頻道上使用 Visual Studio Code 來跟隨上述文章一起學習。 請也查看我們的 YouTube 頻道。

YouTube Video