使用 Cookie 实现认证跳转功能

news2025/5/17 8:08:21

使用 Cookie 实现认证跳转功能的实践与解析

在 Web 开发中,用户身份认证是一个基础而关键的功能点。本文将通过一个简单的前后端示例系统,介绍如何基于 Cookie 实现 Token 保存与自动跳转认证的功能,并结合 Cookie 与 Header 的区别、使用场景、安全性等维度做全面分析。


一、Cookie 和 Header 的区别

项目CookieHeader
定义存储在浏览器中的字段,用于保持用户状态HTTP 请求/响应的元数据,描述请求数据信息
默认行为每次同域同路径请求时自动被浏览器附加到请求中需要开发者手动在请求中配置
存储可持久存储在本地浏览器每次请求时重新传送
与 JS 的关系如果设置 HttpOnly ,JS 无法读取JS 可以自由操作 Header
通用场景登录状态保持,身份声明JWT Token、代理等信息传递

总结:

Cookie 更适合于 Web 应用自动附带的状态保持,Header 则适用于前后端分离、接口授权等场景。

此外,如果系统是前后端分离或移动端调用 API,推荐使用 Header + Bearer Token 的方式;而传统 Web 系统则更偏好基于 Cookie 的方案,方便浏览器自动携带状态。


二、功能需求简述

当前系统需求并非完整用户系统登录,而是一个基于输入 Token 的快速标记机制,满足以下目标:

  • 前端提供 Token(如 email)

  • 后端生成对应的 JWT 并存入浏览器 Cookie

  • 后续访问页面时:

    • 自动读取 Cookie 中的 JWT
    • 后端解析 JWT,确认身份合法则自动跳转
    • 前端弹出提示当前用户 Token 和解析出的信息

在这里插入图片描述


三、项目结构说明

本项目基于 Spring Boot 构建,包含前后端组件,结构如下:

MyTestJava
├── src/
│   └── main/
│       ├── java/
│       │   └── org.example/
│       │       ├── Main.java                     // SpringBoot 启动类
│       │       ├── controller/
│       │       │   └── TokenEntryController.java // 控制器:处理接口请求
│       │       └── util/
│       │           └── JwtUtils.java             // 工具类:生成/解析 JWT
│       └── resources/
│           ├── static/
│           │   └── index.html                    // 前端页面
│           └── application.properties            // 配置文件
├── pom.xml                                       // Maven 配置

说明:

  • JwtUtils:JWT 的封装生成器,负责 create / parse
  • TokenEntryController:接口控制器,处理前端发送的 token 保存请求和验证请求
  • index.html:纯前端展示页面,包含输入框与登录判断逻辑
  • application.properties:可配置端口、秘钥等

四、代码实现分析

📖 JWT 工具类 JwtUtils

@Component
public class JwtUtils {
    private static final String SECRET = "cT9gHD9Myp&Jz@3E*U2a%Ld!Fg#xZvPf";
    private static final Key KEY = Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8));
    private static final long EXPIRATION = 30 * 24 * 60 * 60 * 1000L; // 30天

    public String createToken(String email) {
        return Jwts.builder()
                .setSubject(email)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
                .signWith(KEY)
                .compact();
    }

    public String parseToken(String token) {
        try {
            return Jwts.parserBuilder()
                    .setSigningKey(KEY)
                    .build()
                    .parseClaimsJws(token)
                    .getBody()
                    .getSubject();
        } catch (JwtException e) {
            return null;
        }
    }
}

说明:

  • SECRET 是服务端自定义加密密钥,推荐保存在配置文件中;
  • 可使用 Keys.secretKeyFor(SignatureAlgorithm.HS256) 动态生成,但不适合生产,因为服务重启后旧 token 将无法解析。

📋 Controller: TokenEntryController

@RestController
@RequestMapping("/api")
public class TokenEntryController {

    @Autowired
    private JwtUtils jwtUtils;

    @PostMapping("/token")
    public ResponseEntity<Map<String, Object>> saveToken(@RequestBody Map<String, String> payload,
                                                         HttpServletResponse response) {
        String email = payload.get("token");
        String token = jwtUtils.createToken(email);

        Cookie cookie = new Cookie("login_token", token);
        cookie.setHttpOnly(true);
        cookie.setPath("/");
        cookie.setMaxAge(30 * 24 * 60 * 60);
        response.addCookie(cookie);

        return ResponseEntity.ok(Map.of(
                "status", "success",
                "redirectUrl", "https://www.baidu.com"
        ));
    }

    @GetMapping("/entry")
    public ResponseEntity<Map<String, Object>> checkToken(@CookieValue(value = "login_token", required = false) String token) {
        String email = jwtUtils.parseToken(token);
        if (email != null) {
            return ResponseEntity.ok(Map.of(
                    "status", "success",
                    "email", email,
                    "redirectUrl", "https://www.baidu.com"
            ));
        } else {
            return ResponseEntity.ok(Map.of("status", "fail"));
        }
    }
}

📄 前端 HTML 逻辑

index.html 使用原生 JavaScript 与 Spring Boot 后端交互。

<input type="text" id="tokenInput" placeholder="请输入 Email" />
<button onclick="sendToken()">发送</button>

<script>
function sendToken() {
    const token = document.getElementById("tokenInput").value;
    fetch("/api/token", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ token })
    })
    .then(res => res.json())
    .then(data => {
        if (data.status === "success") {
            window.location.href = data.redirectUrl;
        }
    });
}

window.onload = function () {
    fetch("/api/entry")
        .then(res => res.json())
        .then(body => {
            if (body.status === "success") {
                const token = getCookie("login_token");
                alert("已登录\nToken: " + token + "\nEmail: " + body.email);
                window.location.href = body.redirectUrl;
            }
        });
};

function getCookie(name) {
    const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
    return match ? decodeURIComponent(match[2]) : null;
}
</script>

五、总结

  • Cookie 和 Header 各有优势,要根据场景选择
  • 使用 Cookie 可以自动附加身份信息,适合 Web 项目
  • JWT 分布系统轻量、无状态、可扩展
  • 固定 KEY 应该保存在配置文件中,而非随机生成
  • 浏览器无法读取 HttpOnly Cookie,确保安全性;如需前端读 token,请将 HttpOnly = false

附录:完整文件(可自行补全代码)

Spring Boot 项目目录结构参考

src/main/java/org/example/
├── controller/
│   └── LoginController.java      # 登录与验证码相关接口
├── model/
│   └── User.java                 # 用户模型类
├── service/
│   └── UserService.java          # 登录逻辑与验证码缓存管理
├── util/
│   └── EmailSender.java          # 邮件发送工具类
└── Main.java                     # SpringBoot 启动类

src/main/resources/
├── static/index.html             # 前端测试页面
└── application.properties        # 邮件 + Redis + DB 配置项

pom.xml ✅

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>MyTestJava</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <!-- Spring Boot 父项目 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.3</version>
        <relativePath/>
    </parent>

    <dependencies>
        <!-- Spring Boot Web 模块(包含内嵌 Tomcat) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Boot 开发工具模块 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- JWT 核心 API -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.5</version>
        </dependency>

        <!-- JWT 实现类 -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>

        <!-- JWT 序列化/反序列化 -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>

        <!-- Jakarta Servlet -->
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>6.0.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

index.html ✅

<!DOCTYPE html>
<html lang="zh">
    <head>
        <meta charset="UTF-8">
        <title>Token 验证</title>
        <style>
            body {
                background-color: #f0f2f5;
                font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
                display: flex;
                justify-content: center;
                align-items: center;
                height: 100vh;
                margin: 0;
            }

            .container {
                background-color: white;
                padding: 30px 40px;
                border-radius: 12px;
                box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
                text-align: center;
            }

            .input-group {
                display: flex;
                align-items: center;
                justify-content: center;
                gap: 10px;
            }

            input[type="text"] {
                padding: 10px;
                width: 220px;
                font-size: 16px;
                border: 1px solid #ccc;
                border-radius: 6px;
            }

            button {
                padding: 10px 20px;
                font-size: 16px;
                background-color: #1890ff;
                color: white;
                border: none;
                border-radius: 6px;
                cursor: pointer;
                transition: background-color 0.3s ease;
            }

            button:hover {
                background-color: #40a9ff;
            }
        </style>
    </head>

    <body>
        <div class="container">
            <div class="input-group">
                <label for="token-input">
                    <input type="text" id="token-input" placeholder="输入 Token" />
                </label>
                <button onclick="sendToken()">发送</button>
            </div>
        </div>

        <script>
            function sendToken() {
                const token = document.getElementById("token-input").value;
                fetch("/api/token", {
                    method: "POST",
                    headers: {
                        "Content-Type": "application/json"
                    },
                    body: JSON.stringify({ token })
                })
                .then(response => response.json())
                .then(data => {
                    if (data.status === "success") {
                        window.location.href = data.redirectUrl;
                    } else {
                        alert("Token 无效");
                    }
                });
            }

            // 页面加载后自动访问 entry 进行判断
            window.onload = function () {
                fetch("/api/entry", { method: "GET" })
                .then(response => {
                    if (!response.ok) return null;
                    return response.json();
                })
                .then(body => {
                    if (body && body.status === "success") {
                        alert("\n解析出的Email是:\n" + body.email);
                        window.location.href = body.redirectUrl;
                    }
                })
                .catch(err => {
                    console.error("检查登录状态异常:", err);
                });
            };
        </script>
    </body>
</html>

Main.java ✅

package org.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * ==================================================
 * This class ${NAME} is responsible for [功能描述].
 *
 * @author darker
 * @version 1.0
 * ==================================================
 */

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

JwtUtils.java ✅

package org.example.util;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Date;

/**
 * ==================================================
 * This class JwtUtils is responsible for [功能描述].
 *
 * @author darker
 * @version 1.0
 * ==================================================
 */

@Component
public class JwtUtils {
    private static final String SECRET = "cT9gHD9Myp&Jz@3E*U2a%Ld!Fg#xZvPf";
    private static final Key KEY = Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8));
    private static final long EXPIRATION = 30 * 24 * 60 * 60 * 1000L; // 30天

    public String createToken(String email) {
        return Jwts.builder()
                .setSubject(email)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
                .signWith(KEY)
                .compact();
    }

    public String parseToken(String token) {
        try {
            return Jwts.parserBuilder()
                    .setSigningKey(KEY)
                    .build()
                    .parseClaimsJws(token)
                    .getBody()
                    .getSubject();
        } catch (JwtException e) {
            return null; // 无效/过期
        }
    }
}

TokenEntryController.java ✅

package org.example.controller;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.example.util.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/**
 * ==================================================
 * This class TokenEntryController is responsible for [功能描述].
 *
 * @author draker
 * @version 1.0
 * ==================================================
 */

@RestController
@RequestMapping("/api")
public class TokenEntryController {

    @Autowired
    private JwtUtils jwtUtils;

    /**
     * 创建 Token 并写入 Cookie
     */
    @PostMapping("/token")
    public ResponseEntity<Map<String, Object>> createToken(@RequestBody Map<String, String> payload,
                                                           HttpServletResponse response) {
        String identity = payload.get("token"); // 可以是 email、userId 等

        Map<String, Object> result = new HashMap<>();
        if (identity == null || identity.isEmpty()) {
            result.put("status", "fail");
            return ResponseEntity.badRequest().body(result);
        }

        String token = jwtUtils.createToken(identity);

        Cookie cookie = new Cookie("login_token", token);
        cookie.setPath("/");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(30 * 24 * 60 * 60); // 30 天
        response.addCookie(cookie);

        result.put("status", "success");
        result.put("redirectUrl", "https://www.baidu.com");
        return ResponseEntity.ok(result);
    }

    /**
     * 检查 Cookie 中的 Token 并验证跳转
     */
    @GetMapping("/entry")
    public ResponseEntity<Map<String, Object>> checkToken(@CookieValue(value = "login_token", required = false) String token) {
        Map<String, Object> result = new HashMap<>();

        if (token != null) {
            String email = jwtUtils.parseToken(token);
            if (email != null) {
                result.put("status", "success");
                result.put("email", email);
                result.put("redirectUrl", "https://www.baidu.com");
                return ResponseEntity.ok(result);
            }
        }

        result.put("status", "fail");
        return ResponseEntity.ok(result);
    }
}

希望本文对 Cookie 和 Header 在实际进程中的使用有所启发,也为基于 Spring Boot 实现轻量登录认证提供思路。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2377482.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LED接口设计

一个LED灯有3种控制状态&#xff0c;常亮、常灭和闪烁&#xff0c;要做到这种控制最简单的一种方法是使用任何一款处理器的普通IO去控制。 用IO控制方式有两种&#xff0c;一种是高有效&#xff0c;如下图1所示IO口为高电平时LED亮&#xff0c;IO为低电平时LED不亮。IO口出一个…

SpringBoot项目使用POI-TL动态生成Word文档

近期项目工作需要动态生成Word文档的需求&#xff0c;特意调研了动态生成Word的技术方案。主要有以下两种&#xff1a; 第一种是FreeMarker模板来进行填充&#xff1b;第二种是POI-TL技术使用Word模板来进行填充&#xff1b; 以下是关于POI-TL的官方介绍 重点关注&#xff1…

YOLOv3深度解析:多尺度特征融合与实时检测的里程碑

一、YOLOv3的诞生&#xff1a;继承与突破的起点 YOLOv3作为YOLO系列的第三代算法&#xff0c;于2018年由Joseph Redmon等人提出。它在YOLOv2的基础上&#xff0c;针对小目标检测精度低、多类别标签预测受限等问题进行了系统性改进。通过引入多尺度特征图检测、残差网络架构和独…

uniapp-商城-60-后台 新增商品(属性的选中和页面显示)

前面添加了属性&#xff0c;添加属性的子级项目。也分析了如何回显&#xff0c;但是在添加新的商品的时&#xff0c;我们也同样需要进行选择&#xff0c;还要能正常的显示在界面上。下面对页面的显示进行分析。 1、界面情况回顾 属性显示其实是个一嵌套的数据显示。 2、选中的…

虹科技术 | 简化汽车零部件测试:LIN/CAN总线设备的按键触发功能实现

汽车零部件测试领域对操作的便捷性要求越来越高&#xff0c;虹科Baby-LIN-RC系列产品为这一需求提供了完美的解决方案。从基础的按键设置到高级的Shift键应用&#xff0c;本文将一步步引导您了解虹科Baby-LIN-RC系列产品的智能控制之道。 虹科Baby-LIN-3-RC 想象一下&#xff0…

单片机ESP32天气日历闹铃语音播报

自制Arduino Esp32 单片机 可以整点语音播报&#xff0c;闹铃语音播报&#xff0c;农历显示&#xff0c;白天晚上天气&#xff0c;硬件有 Esp32&#xff0c;ST7789显示屏&#xff0c;Max98357 喇叭驱动&#xff0c;小喇叭一枚。有需要源码的私信我。#单片机 #闹钟 #嵌入式 #智能…

如何解决LCMS 液质联用液相进样器定量环漏液问题

以下是解决安捷伦1260液相色谱仪为例的进样器定量环漏液问题的一些方法&#xff1a;视频操作 检查相关部件 检查定量环本身&#xff1a;观察定量环是否有破损、裂纹或变形等情况。如果发现定量环损坏&#xff0c;需及时更换。检查密封垫&#xff1a;查看进样阀的转子密封垫、计…

服务器内部可以访问外部网络,docker内部无法访问外部网络,只能docker内部访问

要通过 iptables 将容器中的特定端口请求转发到特定服务器&#xff0c;你需要设置 DNAT&#xff08;目标地址转换&#xff09;规则。以下是详细步骤&#xff1a; 假设场景 容器端口: 8080&#xff08;容器内服务监听的端口&#xff09;目标服务器: 192.168.1.100&#xff08;请…

PCIe Switch 问题点

系列文章目录 文章目录 系列文章目录完善PCIe Retimer Overview Document OutlineSwitch 维度BroadComMicroChipAsmedia 祥硕Cyan其他 完善 Functional block diagram&#xff0c;功能框图Key Features and Benefits&#xff0c;主要功能和优点Fabric 链路Multi-root PCIe Re…

开源轻量级地图解决方案leaflet

Leaflet 地图&#xff1a;开源轻量级地图解决方案 Leaflet 是一个开源的 JavaScript 库&#xff0c;用于在网页中嵌入交互式地图。它以轻量级、灵活性和易用性著称&#xff0c;适用于需要快速集成地图功能的项目。以下是关于 Leaflet 的详细介绍和使用指南。 1. Leaflet 的核心…

Flutter目录结构介绍、入口、Widget、Center组件、Text组件、MaterialApp组件、Scaffold组件

目录 1. 创建Flutter项目 1.1使用Android Studio创建Flutter项目 1.2 使用命令行创建Flutter项目 2. Flutter项目介绍 2.1所有代码都在lib目录下编写 2.1 pubspec.yaml 依赖库/图片的引用 ​编辑 3. 运行项目 4. 编写mian.dart文件 4.1 使用MaterialApp 和 Scaffold两个组件…

如何实现金蝶云星空到MySQL的数据高效集成

金蝶云星空数据集成到MySQL的技术案例分享 在企业信息化建设中&#xff0c;数据的高效流动和准确处理是关键。本文将聚焦于一个具体的系统对接集成案例&#xff1a;金蝶云星空的数据集成到MySQL&#xff0c;方案名称为“xsck-2金蝶销售出库-->mysql”。通过这一案例&#x…

院校机试刷题第四天:1911反转公约数、1702十六进制不进位加法

一、1911反转公约数 1.题目描述 2.解题思路 两个关键点&#xff1a;1.如何把数字反转&#xff0c;2.如何求最大公约数。 反转&#xff1a;用字符串形式存储&#xff0c;定义一个新的字符串倒序存储反转之后的字符串&#xff0c;将字符串按位转换位数字。 求最大公约数&…

Redis解析

Redis解析 一、单线程模型 redis在io层面是多线程的&#xff0c;在数据处理层面是单线程的。 多线程一般用于&#xff1a; 关闭连接删除/淘汰内存网络IO 1.1 io多路复用 redis使用nio&#xff08;select、poll、epoll&#xff09;的方式处理socket 主线程负责接收建立连接…

2025年Ai写PPT工具推荐,这5款Ai工具可以一键生成专业PPT

上个月给客户做产品宣讲时&#xff0c;我对着空白 PPT 页面熬到凌晨一点&#xff0c;光是调整文字排版就改了十几版&#xff0c;最后还是被吐槽 "内容零散没重点"。后来同事分享了几款 ai 写 PPT 工具&#xff0c;试完发现简直打开了新世界的大门 —— 不用手动写大纲…

css:倒影倾斜效果

这是需要实现的效果&#xff0c;平时用的比较多的是添加阴影&#xff0c;是box-shadow&#xff0c;而添加倒影是box-reflect&#xff0c;需要注意的是box-reflect需要添加浏览器前缀&#xff0c;比如我用的谷歌浏览器&#xff0c;要加-webkit-才能生效。 -webkit-box-reflect:…

语音识别——通过PyAudio录入音频

PyAudio 是一个用于处理音频的 Python 库&#xff0c;它提供了录制和播放音频的功能。通过 PyAudio&#xff0c;可以轻松地从麦克风或其他音频输入设备录制音频&#xff0c;并将其保存为文件或进行进一步处理。 安装 PyAudio 在使用 PyAudio 之前&#xff0c;需要先安装它。可…

五月月报丨MaxKB在教育行业的应用进展与典型场景

在2025年的3月和4月的“用户应用月度报告”中&#xff0c;MaxKB开源项目组相继总结了MaxKB开源项目在政府、公共事业、教育、医疗以及企事业单位的应用情况。毫无疑问&#xff0c;在DeepSeek等国产大模型被各行各业的用户广泛接受之后&#xff0c;AI应用建设并运营的步伐也在显…

【流程控制结构】

流程控制结构 流程控制结构1、顺序结构2、选择结构if基本选择结构if else语法多重if语法嵌套if语法switch选择结构 3、循环结构循环结构while循环结构程序调试for循环跳转语句区别 流程控制结构 1、顺序结构 流程图 优先级 2、选择结构 if基本选择结构 单if 语法 if&…

PowerBI基础

一、前言 在当今数据驱动的时代&#xff0c;如何高效地整理、分析并呈现数据&#xff0c;已成为企业和个人提升决策质量的关键能力。Power BI 作为微软推出的强大商业智能工具&#xff0c;正帮助全球用户将海量数据转化为直观、动态的可视化洞察。数据的世界充满可能性&#xf…