使用 Cookie 实现认证跳转功能
使用 Cookie 实现认证跳转功能的实践与解析
在 Web 开发中,用户身份认证是一个基础而关键的功能点。本文将通过一个简单的前后端示例系统,介绍如何基于 Cookie 实现 Token 保存与自动跳转认证的功能,并结合 Cookie 与 Header 的区别、使用场景、安全性等维度做全面分析。
一、Cookie 和 Header 的区别
项目 | Cookie | Header |
---|---|---|
定义 | 存储在浏览器中的字段,用于保持用户状态 | 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 / parseTokenEntryController
:接口控制器,处理前端发送的 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 {@Autowiredprivate 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 {@Autowiredprivate 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 实现轻量登录认证提供思路。
相关文章:

使用 Cookie 实现认证跳转功能
使用 Cookie 实现认证跳转功能的实践与解析 在 Web 开发中,用户身份认证是一个基础而关键的功能点。本文将通过一个简单的前后端示例系统,介绍如何基于 Cookie 实现 Token 保存与自动跳转认证的功能,并结合 Cookie 与 Header 的区别、使用场…...
Reth(冗余以太网接口) 和Bridge-Aggregation(链路聚合接口)区别
Reth(Redundant Ethernet)与Bridge-Aggregation是H3C设备中两种不同的接口技术,主要区别体现在工作原理、应用场景及配置特性上。以下是详细对比分析: 定义与类型 Reth(冗余以太网接口) 类型:…...
(面试)Android各版本新特性
Android 6.0 (Marshmallow, API 23) 运行时权限管理:用户可在应用运行时动态授予或拒绝权限,取代安装时统一授权4。Doze模式与应用待机:优化后台耗电,延长设备续航5。指纹识别支持:原生API支持指纹身份验证。 Android…...
算法基础 -- 小根堆构建的两种方式:上浮法与下沉法
小根堆构建的两种方式:上浮法与下沉法 在构建小根堆(Min-Heap)时,通常有两种常见的构建方式: 上浮建堆(逐个插入,上浮调整)下沉建堆(Heapify 自底向上,下沉…...

LED接口设计
一个LED灯有3种控制状态,常亮、常灭和闪烁,要做到这种控制最简单的一种方法是使用任何一款处理器的普通IO去控制。 用IO控制方式有两种,一种是高有效,如下图1所示IO口为高电平时LED亮,IO为低电平时LED不亮。IO口出一个…...
西安前端面试
面试1 1.vue2和vue3的原理及区别 2.伪数组 3.对箭头函数怎么理解的 4.vue父子组件传值的几种方式 5.对Promise的理解 面试2 1.两个升序数组实现合并升序排序 2.数组拍平[3, [[7, [1, 5]], 4], 8, [6]] 面试3 1.let var const的区别,什么时候const能改变 …...

SpringBoot项目使用POI-TL动态生成Word文档
近期项目工作需要动态生成Word文档的需求,特意调研了动态生成Word的技术方案。主要有以下两种: 第一种是FreeMarker模板来进行填充;第二种是POI-TL技术使用Word模板来进行填充; 以下是关于POI-TL的官方介绍 重点关注࿱…...
java高效实现爬虫
一、前言 在Web爬虫技术中,Selenium作为一款强大的浏览器自动化工具,能够模拟真实用户操作,有效应对JavaScript渲染、Ajax加载等复杂场景。而集成代理服务则能够解决IP限制、地域访问限制等问题。本文将详细介绍如何利用JavaSelenium快代理实…...

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

uniapp-商城-60-后台 新增商品(属性的选中和页面显示)
前面添加了属性,添加属性的子级项目。也分析了如何回显,但是在添加新的商品的时,我们也同样需要进行选择,还要能正常的显示在界面上。下面对页面的显示进行分析。 1、界面情况回顾 属性显示其实是个一嵌套的数据显示。 2、选中的…...

虹科技术 | 简化汽车零部件测试:LIN/CAN总线设备的按键触发功能实现
汽车零部件测试领域对操作的便捷性要求越来越高,虹科Baby-LIN-RC系列产品为这一需求提供了完美的解决方案。从基础的按键设置到高级的Shift键应用,本文将一步步引导您了解虹科Baby-LIN-RC系列产品的智能控制之道。 虹科Baby-LIN-3-RC 想象一下࿰…...

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

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

服务器内部可以访问外部网络,docker内部无法访问外部网络,只能docker内部访问
要通过 iptables 将容器中的特定端口请求转发到特定服务器,你需要设置 DNAT(目标地址转换)规则。以下是详细步骤: 假设场景 容器端口: 8080(容器内服务监听的端口)目标服务器: 192.168.1.100(请…...
机器学习中的特征工程:解锁模型性能的关键
在机器学习领域,模型的性能往往取决于数据的质量和特征的有效性。尽管深度学习模型在某些任务中能够自动提取特征,但在大多数传统机器学习任务中,特征工程仍然是提升模型性能的关键环节。本文将深入探讨特征工程的重要性、常用方法以及在实际…...
JAVA:Spring Boot 集成 RDF4J 实现欺诈检测的技术指南
1、简述 在大数据、知识图谱和金融风控等领域,RDF(Resource Description Framework) 是一种用于表示和查询关联数据的强大工具。RDF4J 是一个流行的 Java 库,用于操作 RDF 数据集,并支持 SPARQL 查询,能够帮助我们进行复杂的欺诈检测。 项目的核心功能: RDF 数据存储:…...
spring boot 注解
spring boot 注解 spring 会把被注解Controller、Service、Repository、Component 标注的类 纳入Spring容器中进行管理。 第7章会讲解 IoC 容器。 Controller。 它用于标注控制器层,在MVC 开发模式中代表C(控制器)。 Model View Controller Controlle…...
Spring框架的事务管理
配置文件的方式 <!--第二种写法:使用提供标签的方式--> <context:property-placeholder location"classpath:jd.properties"/><!--加载属性的文件(使用开源连接池)--> <bean id"dataSource" class"com.a…...
Spring MVC 中请求处理流程及核心组件解析
在 Spring MVC 中,请求从客户端发送到服务器后,需要经过一系列组件的处理才能最终到达具体的 Controller 方法。这个过程涉及多个核心组件和复杂的映射机制,下面详细解析其工作流程: 1. 核心组件与请求流程 Spring MVC 的请求处…...

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

开源轻量级地图解决方案leaflet
Leaflet 地图:开源轻量级地图解决方案 Leaflet 是一个开源的 JavaScript 库,用于在网页中嵌入交互式地图。它以轻量级、灵活性和易用性著称,适用于需要快速集成地图功能的项目。以下是关于 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的技术案例分享 在企业信息化建设中,数据的高效流动和准确处理是关键。本文将聚焦于一个具体的系统对接集成案例:金蝶云星空的数据集成到MySQL,方案名称为“xsck-2金蝶销售出库-->mysql”。通过这一案例&#x…...
Web性能优化的未来:边缘计算、AI与新型渲染架构
一、边缘计算与性能优化深度整合 1.1 边缘节点计算卸载策略 • 智能任务分割:将非关键路径计算卸载到边缘节点 // 客户端代码 const edgeTask = new EdgeTask(image-processing); edgeTask.postMessage(imageData, {transfer...

院校机试刷题第四天:1911反转公约数、1702十六进制不进位加法
一、1911反转公约数 1.题目描述 2.解题思路 两个关键点:1.如何把数字反转,2.如何求最大公约数。 反转:用字符串形式存储,定义一个新的字符串倒序存储反转之后的字符串,将字符串按位转换位数字。 求最大公约数&…...
java输入输出类
父类 子类--->System.in(实例类) InputStream(抽象类,所有输入流的父类)|--->FileInputStream---->System.out(实例类) OutpustStream(抽象类,所有输出流的父类)|----> FileOutputStream----&…...

Redis解析
Redis解析 一、单线程模型 redis在io层面是多线程的,在数据处理层面是单线程的。 多线程一般用于: 关闭连接删除/淘汰内存网络IO 1.1 io多路复用 redis使用nio(select、poll、epoll)的方式处理socket 主线程负责接收建立连接…...
golang -- 认识channel底层结构
channel channel是golang中用来实现多个goroutine通信的管道(goroutine之间的通信机制),底层是一个叫做hchan的结构体,定义在runtime包中 type hchan struct {qcount uint // 循环数组中的元素个数(通道…...

2025年Ai写PPT工具推荐,这5款Ai工具可以一键生成专业PPT
上个月给客户做产品宣讲时,我对着空白 PPT 页面熬到凌晨一点,光是调整文字排版就改了十几版,最后还是被吐槽 "内容零散没重点"。后来同事分享了几款 ai 写 PPT 工具,试完发现简直打开了新世界的大门 —— 不用手动写大纲…...
对称二叉树的判定:双端队列的精妙应用
一、题目解析 题目描述 给定一个二叉树,检查它是否是镜像对称的。例如,二叉树 [1,2,2,3,4,4,3] 是对称的: 1/ \2 2/ \ / \ 3 4 4 3而 [1,2,2,null,3,null,3] 则不是镜像对称的: 1/ \2 2\ \3 3问题本质 判断一棵二叉…...