使用 Cookie 实现登录登出功能案例

由于http 是一种无状态协议,请求一次和百次的内容都一致 ,*无状态是指协议本身不会保留客户端与服务器之间的交互状态信息*,每个HTTP请求都是独立的,服务器不会默认记录之前的请求内容或上下文。

Cookie 正是为了解决这个问题而诞生的,它在 HTTP 协议的框架下实现了"有状态"的通信。它通过在HTTP报文头部添加特定字段,实现状态保持。

一、前期准备

  1. 初始化项目

    bash 复制代码
    mkdir cookie-demo
    cd cookie-demo
    npm init -y
    npm install express cookie-parser body-parser
  2. 创建并配置服务

    js 复制代码
    const express = require("express");
    const cookieParser = require('cookie-parser'); // 引入
    const bodyParser = require('body-parser'); // 解析post请求参数
    const PORT = 3000;
    
    const app = express();
    // 使用中间件
    app.use(bodyParser.urlencoded({ extended: true }));
    app.use(cookieParser());
    
    // 模拟用户数据库
    const users = [
      { id: 1, username: 'admin', password: 'admin123' },
      { id: 2, username: 'user', password: 'user123' }
    ];
    
    // 定义端口
    
    app.listen(PORT, () => {
      console.log(`服务启动成功 http://localhost:${PORT}`);
    });

    二、创建路由

    1. 首页

      访问http://localhost:3000 当cookie中没有用户数据进入登录页,若有数据则进入用户信息页

      这里模拟的是cookie记录之前的登录信息

      js 复制代码
      // 首页路由
      app.get("/", (req, res) => {
        if (req.cookies.userId) {
          // 若cookie中有用户数据 则转跳到用户信息页
          res.redirect("/userIndex");
        } else {
          // 否则进入登录界面
          res.redirect("/login");
        }
      });
    2. 登录路由

      js 复制代码
      // 获取登录界面
      app.get("/login", (req, res) => {
        res.sendFile(__dirname + "/login.html");
      });
      // 登录处理
      app.post("/login", (req, res) => {
        const { username, password } = req.body;
      
        // 模拟数据库查找
        const user = users.find(
          (u) => u.username === username && u.password === password
        );
      
        if (user) {
          res.cookie("userId", user.id, {
            maxAge: 24 * 60 * 60 * 1000,
            httpOnly: true,
            sameSite: "strict",
          });
          // 发送请求成功
          res.send({ code: 0, msg: "登录成功" });
        } else {
          res.status(401).send("用户名或密码错误");
        }
      });

      登录界面:

    3. 用户路由

      js 复制代码
      // 获取用户界面
      app.get("/userIndex", (req, res) => {
        res.sendFile(__dirname + "/user.html");
      });
      
      // 获取用户资料
      app.get("/user", (req, res) => {
        const userId = parseInt(req.cookies.userId);
        // 查询数据库
        const user = users.find((u) => u.id === userId);
      
        // 返回数据
        res.send({username:user.username,id:user.id});
      });

      用户信息页:

    4. 登出

      登出后转跳到登录页

      js 复制代码
      // 登出
      app.get('/logout',(req,res)=>{
          res.clearCookie('userId')
          res.redirect('/login')
      })

    三、操作流程

    1. 访问 http://localhost:3000 会自动跳转到登录页

    2. 使用以下凭证登录:

      • 用户名: admin,密码: admin123
      • 或 用户名: user,密码: user123
    3. 登录成功后进入用户信息页(如上所示)

    4. 上述操作过后,cookie已经保存在浏览器中了,下次访问http://localhost:3000后端校验cookie后会直接转跳到用户信息页

    5. 点击 Logout 退出登录会删除浏览器中的cookie

    四、代码

    1. servlet.js 后端服务

      js 复制代码
      const express = require("express");
      const cookieParser = require("cookie-parser"); // 引入
      const bodyParser = require("body-parser"); // 解析post请求参数
      const PORT = 3000;
      
      const app = express();
      app.use(express.json());
      app.use(express.urlencoded({ extended: true }));
      // 使用中间件
      app.use(bodyParser.urlencoded({ extended: true }));
      app.use(cookieParser());
      
      // 模拟用户数据库
      const users = [
        { id: 1, username: "admin", password: "admin123" },
        { id: 2, username: "user", password: "user123" },
      ];
      
      // 定义端口
      app.listen(PORT, () => {
        console.log(`服务启动成功 http://localhost:${PORT}`);
      });
      
      // 首页路由
      app.get("/", (req, res) => {
        if (req.cookies.userId) {
          // 若cookie中有用户数据 则转跳到用户页面
          res.redirect("/userIndex");
        } else {
          // 否则进入登录界面
          res.redirect("/login");
        }
      });
      
      // 获取登录界面
      app.get("/login", (req, res) => {
        res.sendFile(__dirname + "/login.html");
      });
      
      // 获取用户界面
      app.get("/userIndex", (req, res) => {
        res.sendFile(__dirname + "/user.html");
      });
      
      // 登录处理
      app.post("/login", (req, res) => {
        const { username, password } = req.body;
      
        // 模拟数据库查找
        const user = users.find(
          (u) => u.username === username && u.password === password
        );
      
        if (user) {
          res.cookie("userId", user.id, {
            maxAge: 24 * 60 * 60 * 1000,
            httpOnly: true,
            sameSite: "strict",
          });
          // 发送请求成功
          res.send({ code: 0, msg: "登录成功" });
        } else {
          res.status(401).send("用户名或密码错误");
        }
      });
      
      // 用户资料
      app.get("/user", (req, res) => {
        const userId = parseInt(req.cookies.userId);
        if (!userId) {
          // 页面转跳
          res.redirect('/login')
          return
        }
        // 查询数据库
        const user = users.find((u) => u.id === userId);
      
        // 返回数据
        res.send({username:user.username,id:user.id});
      });
      
      
      // 登出
      app.get('/logout',(req,res)=>{
          res.clearCookie('userId')
          res.redirect('/login')
      })
    2. 登录页login.html

      html 复制代码
        <body>
          <div class="login-container">
            <h1 class="login-title">用户登录</h1>
            <form id="loginForm" class="login-form">
              <div class="form-group">
                <label for="username" class="form-label">用户名</label>
                <input
                  type="text"
                  id="username"
                  name="username"
                  class="form-input"
                  placeholder="请输入用户名"
                  required
                />
              </div>
              <div class="form-group">
                <label for="password" class="form-label">密码</label>
                <input
                  type="password"
                  id="password"
                  name="password"
                  class="form-input"
                  placeholder="请输入密码"
                  required
                />
              </div>
              <div class="form-actions">
                <button type="submit" class="btn-login">登录</button>
                <button type="reset" class="btn-reset">重置</button>
              </div>
              <div id="error-message" class="error-message"></div>
            </form>
          </div>
      
          <script>
            // 简单的表单提交处理
            document
              .getElementById("loginForm")
              .addEventListener("submit", async (e) => {
                e.preventDefault();
                const username = document.getElementById("username").value;
                const password = document.getElementById("password").value;
                const errorElement = document.getElementById("error-message");
      
                // 这里可以添加实际的登录验证逻辑
                if (username && password) {
                  errorElement.textContent = "登录中...";
                  errorElement.style.color = "#137333";
                  // 模拟登录请求
                  const res = await fetch("http://localhost:3000/login", {
                    method: "POST",
                    headers: {
                      "Content-Type": "application/json",
                    },
                    body: JSON.stringify({
                      username: username,
                      password: password,
                    }),
                  });
                  const data = await res.json();
                  console.log(data);
                  if (data.code === 0) {
                    // 登录成功后,跳转到用户界面
                    window.location.href = "http://localhost:3000/userIndex";
                  }
                } else {
                  errorElement.textContent = "请输入用户名和密码";
                }
              });
          </script>
        </body>
    3. 用户信息页

      html 复制代码
        <body>
          <div class="container">
            <div class="header">
              <h1 class="title">用户信息</h1>
              <button class="btn-logout" id="logoutBtn">退出登录</button>
            </div>
      
            <div class="user-card">
              <div class="avatar">
                <span id="avatarInitial">A</span>
              </div>
              <div class="user-info">
                <div class="info-item">
                  <span class="info-label">用户ID:</span>
                  <span class="info-value" id="userId">--</span>
                </div>
                <div class="info-item">
                  <span class="info-label">用户名:</span>
                  <span class="info-value" id="username">--</span>
                </div>
                <div class="info-item">
                  <span class="info-label">登录状态:</span>
                  <span class="info-value" id="loginStatus">在线</span>
                </div>
              </div>
            </div>
      
            <div class="footer">
              <p>© 2023 用户管理系统 - 版权所有</p>
            </div>
          </div>
      
          <script>
            // 从Cookie中获取用户信息
            function getCookie(name) {
              const cookies = document.cookie.split(";");
              for (let cookie of cookies) {
                const [cookieName, cookieValue] = cookie.trim().split("=");
                if (cookieName === name) {
                  return decodeURIComponent(cookieValue);
                }
              }
              return null;
            }
      
            // 检查用户是否已登录
            function checkLogin() {
              const userId = getCookie("userId");
              if (!userId) {
                // window.location.href = 'login.html';
                return false;
              }
              return true;
            }
      
            // 加载用户信息
            async function loadUserInfo() {
              // if (!checkLogin()) return;
      
              // 在实际应用中,这里应该从服务器获取完整用户信息
              const res = await fetch("/user", {
                method: "GET",
                headers: {
                  'Content-Type': 'application/json'
                }
              });
              const data = await res.json();
              console.log(data)
              // 这里为了演示,使用预设数据
              const userData = {
                id: getCookie("userId"),
                username: data.username,
              };
              console.log(userData)
              // 更新页面显示
              document.getElementById("userId").textContent = userData.id;
              document.getElementById("username").textContent = userData.username;
              document.getElementById("avatarInitial").textContent = userData.username
                .charAt(0)
                .toUpperCase();
            }
      
            // 退出登录
            async function logout() {
      
              // // 清除Cookie
              // document.cookie =
              //   "userId=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
              // // 重定向到登录页
              // window.location.href = "login.html";
              const res = await fetch('/logout',{
                  method:'GET',
                  headers: {
                      'Content-Type': 'application/json'
                  }
              })
            }
      
            // 页面加载完成后执行
            document.addEventListener("DOMContentLoaded", function () {
              loadUserInfo();
              document.getElementById("logoutBtn").addEventListener("click", logout);
            });
          </script>
        </body>
相关推荐
崔庆才丨静觅15 分钟前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60611 小时前
完成前端时间处理的另一块版图
前端·github·web components
KYGALYX1 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了1 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅1 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅2 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
爬山算法2 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
崔庆才丨静觅2 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment2 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅2 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端