JWT令牌验证

一、JWT 验证方式详解

JWT(JSON Web Token)的验证核心是确保令牌未被篡改符合业务规则,主要分为以下步骤:

1. 令牌解析与基础校验

收到客户端传递的 JWT 后,首先按 . 分割为三部分:HeaderPayloadSignature

  • Header:解析算法(如 HS256)和令牌类型(固定为 JWT)。
  • Payload:解析标准声明(如 exp 过期时间、iat 签发时间)和自定义声明(如用户 ID)。
2. 签名验证(防篡改)

签名是 JWT 的核心安全屏障。验证逻辑:

  • 使用 Header 中指定的算法(如 HMAC SHA256),用服务端保存的密钥Header.Base64UrlEncode() + "." + Payload.Base64UrlEncode() 重新计算签名。
  • 对比新计算的签名与 JWT 中的 Signature,若不一致则令牌无效(可能被篡改)。
3. 声明校验(业务规则)

验证 Payload 中的声明是否符合业务要求:

  • exp(Expiration Time):令牌过期时间戳,需满足 当前时间 < exp(考虑时钟偏差,如 ±300秒)。
  • iat(Issued At):令牌签发时间,需满足 iat ≤ 当前时间
  • iss(Issuer):令牌签发者,需与服务端配置的签发者(如 https://your-domain.com)一致。
  • aud(Audience):令牌接收方,需与当前服务身份(如 user-service)匹配。
  • 自定义声明:如用户角色(role)、权限(permissions)等,按业务需求验证。
4. 令牌状态校验(可选)

对于需要主动失效的令牌(如用户注销),需维护一个黑名单(如 Redis),验证时检查令牌是否在黑名单中(适用于需要严格控制令牌生命周期的场景)。


二、后端 Java 示例(Spring Boot + JJWT)

以下是基于 Spring Boot 的 JWT 生成与验证接口实现,使用 JJWT 库(Java JWT 工具)。

1. 依赖配置

pom.xml 中添加 JJWT 依赖:

<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.5</version><scope>runtime</scope>
</dependency>
2. JWT 工具类(核心逻辑)
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;@Component
public class JwtUtils {// 从配置文件读取密钥和过期时间(示例:密钥为 "your-256-bit-secret",过期时间 30分钟)@Value("${jwt.secret}")private String secret;@Value("${jwt.expire}")private long expire;// 生成 JWTpublic String generateToken(String userId) {Date now = new Date();Date expireDate = new Date(now.getTime() + expire * 1000); // 转换为毫秒Map<String, Object> claims = new HashMap<>();claims.put("userId", userId); // 自定义声明claims.put("role", "user");   // 自定义角色return Jwts.builder().setClaims(claims)          // 自定义声明.setIssuer("your-domain")   // 签发者(iss).setIssuedAt(now)           // 签发时间(iat).setExpiration(expireDate)  // 过期时间(exp).signWith(SignatureAlgorithm.HS256, secret) // 签名算法+密钥.compact();}// 验证 JWT 并解析声明public Claims validateToken(String token) {try {return Jwts.parser().setSigningKey(secret)    // 使用相同密钥验证.parseClaimsJws(token)    // 解析 JWT.getBody();} catch (ExpiredJwtException e) {throw new RuntimeException("令牌已过期");} catch (UnsupportedJwtException e) {throw new RuntimeException("不支持的令牌类型");} catch (MalformedJwtException e) {throw new RuntimeException("令牌格式错误");} catch (SignatureException e) {throw new RuntimeException("签名验证失败(可能被篡改)");} catch (IllegalArgumentException e) {throw new RuntimeException("令牌为空");}}
}
3. 接口示例(生成与验证)
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;@RestController
@RequestMapping("/auth")
public class AuthController {@Resourceprivate JwtUtils jwtUtils;// 模拟登录接口:返回 JWT@PostMapping("/login")public String login(@RequestParam String username, @RequestParam String password) {// 实际业务中需校验用户名密码(示例直接通过)return jwtUtils.generateToken("123"); // 用户 ID 为 123}// 验证令牌接口(示例)@PostMapping("/validate")public String validate(@RequestHeader("Authorization") String token) {// 提取 Bearer 令牌(去掉 "Bearer " 前缀)String actualToken = token.replace("Bearer ", "");try {Claims claims = jwtUtils.validateToken(actualToken);return "验证成功!用户 ID:" + claims.get("userId");} catch (Exception e) {return "验证失败:" + e.getMessage();}}
}

三、前端/客户端示例(Uniapp、WPF、Qt)

以下示例演示客户端如何携带 JWT 调用后端验证接口(假设后端地址为 http://localhost:8080)。

1. Uniapp(Vue 跨端框架)
<template><view><button @click="login">登录获取 Token</button><button @click="validateToken">验证 Token</button><text>{{ result }}</text></view>
</template><script>
export default {data() {return {token: null,result: ""};},methods: {// 登录获取 Tokenasync login() {const res = await uni.request({url: "http://localhost:8080/auth/login",method: "POST",data: { username: "test", password: "123" }});this.token = res.data;this.result = "Token 获取成功:" + this.token;},// 验证 Token(携带到请求头)async validateToken() {if (!this.token) {this.result = "请先登录获取 Token";return;}const res = await uni.request({url: "http://localhost:8080/auth/validate",method: "POST",header: { Authorization: `Bearer ${this.token}` }});this.result = res.data;}}
};
</script>
2. WPF(.NET 桌面应用)
using System;
using System.Net.Http;
using System.Windows;namespace WpfJwtDemo {public partial class MainWindow : Window {private string _token;private readonly HttpClient _httpClient = new HttpClient();public MainWindow() {InitializeComponent();_httpClient.BaseAddress = new Uri("http://localhost:8080/");}// 登录获取 Tokenprivate async void LoginButton_Click(object sender, RoutedEventArgs e) {var formData = new FormUrlEncodedContent(new[] {new KeyValuePair<string, string>("username", "test"),new KeyValuePair<string, string>("password", "123")});var response = await _httpClient.PostAsync("auth/login", formData);_token = await response.Content.ReadAsStringAsync();ResultText.Text = "Token获取成功:" + _token;}// 验证 Token(携带到请求头)private async void ValidateButton_Click(object sender, RoutedEventArgs e) {if (string.IsNullOrEmpty(_token)) {ResultText.Text = "请先登录获取 Token";return;}_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _token);var response = await _httpClient.PostAsync("auth/validate", null);var result = await response.Content.ReadAsStringAsync();ResultText.Text = result;}}
}
3. Qt(C++ 跨平台框架)
#include <QApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrlQuery>
#include <QMessageBox>class JwtDemo : public QObject {Q_OBJECT
public:JwtDemo(QObject *parent = nullptr) : QObject(parent) {manager = new QNetworkAccessManager(this);connect(manager, &QNetworkAccessManager::finished, this, &JwtDemo::onRequestFinished);}// 登录获取 Tokenvoid login() {QUrl url("http://localhost:8080/auth/login");QUrlQuery query;query.addQueryItem("username", "test");query.addQueryItem("password", "123");QNetworkRequest request(url);request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");manager->post(request, query.toString(QUrl::FullyEncoded).toUtf8());}// 验证 Token(携带到请求头)void validateToken(const QString &token) {QUrl url("http://localhost:8080/auth/validate");QNetworkRequest request(url);request.setRawHeader("Authorization", "Bearer " + token.toUtf8());manager->post(request, ""); // 空 body}private slots:void onRequestFinished(QNetworkReply *reply) {if (reply->error() == QNetworkReply::NoError) {QString result = reply->readAll();if (reply->url().path() == "/auth/login") {currentToken = result;QMessageBox::information(nullptr, "成功", "Token获取成功:" + result);} else if (reply->url().path() == "/auth/validate") {QMessageBox::information(nullptr, "验证结果", result);}} else {QMessageBox::critical(nullptr, "错误", "请求失败:" + reply->errorString());}reply->deleteLater();}private:QNetworkAccessManager *manager;QString currentToken;
};int main(int argc, char *argv[]) {QApplication app(argc, argv);JwtDemo demo;// 模拟点击登录(实际需绑定UI事件)demo.login();// 假设登录后验证(实际需等待登录完成)QTimer::singleShot(2000, [&demo]() {if (!demo.currentToken.isEmpty()) {demo.validateToken(demo.currentToken);}});return app.exec();
}#include "main.moc" // 需包含 moc 文件(Qt 元对象编译)

四、关键说明

  1. 密钥安全:后端密钥(jwt.secret)需严格保密,避免硬编码在代码中(建议通过配置中心或环境变量获取)。
  2. 过期时间exp 需根据业务场景设置(如用户登录态建议 30分钟~1天,敏感操作建议更短)。
  3. 客户端处理:前端需将 JWT 存储在 localStorage(Web)、SecureStorage(移动端)或 注册表(桌面端)中,避免明文存储。
  4. 跨域问题:若前端与后端不同域,需在后端配置 CORS(跨域资源共享)。

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

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

相关文章

一文讲清python、anaconda的安装以及pycharm创建工程

软件下载 Pycharm下载地址&#xff1a; Other Versions - PyCharm anaconda下载地址&#xff1a; https://repo.anaconda.com/archive/Anaconda3-2024.06-1-Windows-x86_64.exe 安装步骤 一、 Python 解释器的安装步骤 安装目录介绍&#xff1a; 二、 Anaconda 安装 2.1 安装步…

Mac如何允许安装任何来源软件?

打开系统偏好设置-安全性与隐私&#xff0c;点击右下角的解锁按钮&#xff0c;选择允许从任何来源。 如果没有这一选项&#xff0c;请到打开终端&#xff0c;输入命令行&#xff1a;sudo spctl --master-disable, 输入命令后回车&#xff0c;输入电脑的开机密码后回车。 返回“…

React Flow 中 Minimap 与 Controls 组件使用指南:交互式小地图与视口控制定制(含代码示例)

本文为《React Agent&#xff1a;从零开始构建 AI 智能体》专栏系列文章。 专栏地址&#xff1a;https://blog.csdn.net/suiyingy/category_12933485.html。项目地址&#xff1a;https://gitee.com/fgai/react-agent&#xff08;含完整代码示​例与实战源&#xff09;。完整介绍…

Windows Ubuntu 目录映射关系

情况一&#xff1a;你是通过 WSL (Windows Subsystem for Linux) 安装 Ubuntu 这是最常见的情况。如果你在 Microsoft Store 安装了 “Ubuntu”&#xff0c;默认就是 WSL。 &#x1f4c1; 目录映射关系如下&#xff1a; 从 Ubuntu&#xff08;WSL&#xff09;访问 Windows&…

双指针法高效解决「移除元素」问题

双指针法高效解决「移除元素」问题 双指针法高效解决「移除元素」问题一、问题描述二、解法解析&#xff1a;双指针法1. 核心思想2. 算法步骤3. 执行过程示例 三、关键点分析四、复杂度分析五、与其他解法的比较1. 快慢指针法2. 本解法的优势 六、实际应用场景七、总结 双指针法…

知识图谱构架

目录 知识图谱构架 一、StanfordNLP 和 spaCy 工具介绍 &#xff08;一&#xff09;StanfordNLP 主要功能 使用示例 &#xff08;二&#xff09;spaCy 主要功能 使用示例 二、CRF 和 BERT 的基本原理和入门 &#xff08;一&#xff09;CRF&#xff08;条件随机场&…

激光三角测量标定与应用

文章目录 1&#xff0c;介绍。2&#xff0c;技术原理3&#xff0c;类型。3.1&#xff0c;直射式3.2&#xff0c;斜射式3.3&#xff0c;两种三角位移传感器特性的比较 4&#xff0c;什么是光片&#xff1f;5&#xff0c;主要的算子。1&#xff0c;create_sheet_of_light_model2&…

高可用消息队列实战:AWS SQS 在分布式系统中的核心解决方案

引言&#xff1a;消息队列的“不可替代性” 在微服务架构和分布式系统盛行的今天&#xff0c;消息队列&#xff08;Message Queue&#xff09; 已成为解决系统解耦、流量削峰、异步处理等难题的核心组件。然而&#xff0c;传统的自建消息队列&#xff08;如RabbitMQ、Kafka&am…

人工智能核心知识:AI Agent 的四种关键设计模式

人工智能核心知识&#xff1a;AI Agent 的四种关键设计模式 一、引言 在人工智能领域&#xff0c;AI Agent&#xff08;人工智能代理&#xff09;是实现智能行为和决策的核心实体。它能够感知环境、做出决策并采取行动以完成特定任务。为了设计高效、灵活且适应性强的 AI Age…

平替BioLegend品牌-Elabscience PE Anti-Mouse Foxp3抗体:流式细胞术中的高效工具,助力免疫细胞分析!”

概述 调节性T细胞&#xff08;Treg&#xff09;在维持免疫耐受和抑制过度免疫反应中发挥关键作用&#xff0c;其标志性转录因子Foxp3&#xff08;Forkhead box P3&#xff09;是Treg功能研究的重要靶点。Elabscience 推出的抗小鼠Foxp3抗体&#xff08;3G3-E&#xff09;&…

编程日志5.13

邻接表的基础代码 #include<iostream> using namespace std; //邻接表的类声明 class Graph {private: //结构体EdgeNode表示图中的边结点,包含顶点vertex、权重weight和指向下一个边结点的指针next struct EdgeNode { int vertex; int weight; …

PowerBI 矩阵实现动态行内容(如前后销售数据)统计数据,以及过滤同时为0的数据

我们有一张活动表 和 一张销售表 我们想实现如下的效果&#xff0c;当选择某个活动时&#xff0c;显示活动前后3天的销售对比图&#xff0c;如下&#xff1a; 实现方法&#xff1a; 1.新建一个表&#xff0c;用于显示列&#xff1a; 2.新建一个度量值&#xff0c;用SELECTEDVA…

Prompt Tuning:高效微调大模型的新利器

Prompt Tuning(提示调优)是什么 Prompt Tuning(提示调优) 是大模型参数高效微调(Parameter-Efficient Fine-Tuning, PEFT)的重要技术之一,其核心思想是通过优化 连续的提示向量(而非整个模型参数)来适配特定任务。以下是关于 Prompt Tuning 的详细解析: 一、核心概念…

杰发科技AC7840——如何把结构体数据写到Dflash中

1. 结构体数据被存放在Pflash中 正常情况下&#xff0c;可以看到全局变量的结构体数据被存放在Pflash中 数字部分存在RAM中 2. 最小编程单位 8字节编程&#xff0c;因此如果结构体存放在Dfalsh中&#xff0c;进行写操作&#xff0c;需要写8字节的倍数 第一种办法&#xff1a;…

CSS 选择器入门

一、CSS 选择器基础&#xff1a;快速掌握核心概念 什么是选择器&#xff1f; CSS 选择器就像 “网页元素的遥控器”&#xff0c;用于定位 HTML 中的特定元素并应用样式。 /* 结构&#xff1a;选择器 { 属性: 值; } */ p { color: red; } /* 选择所有<p>元素&#xff0c;…

Anaconda3安装教程(附加安装包)Anaconda详细安装教程Anaconda3 最新版安装教程

多环境隔离 可同时维护生产环境、开发环境、测试环境&#xff0c;例如&#xff1a; conda create -n ml python3.10 # 创建机器学习环境 conda activate ml # 激活环境三、Anaconda3 安装教程 解压Anaconda3安装包 找到下载的 Anaconda3 安装包&#xff08;.ex…

现代计算机图形学Games101入门笔记(十七)

双向路径追踪 外观建模 散射介质 人的头发不能用在动画的毛发上。 动物的髓质Medulla特别大 双层圆柱模型应用 BSSRDF是BRDF的延伸。 天鹅绒用BRDF不合理&#xff0c;转成散射介质。 法线分布 光追很难处理微表面模型 光在微型细节上&#xff0c;光是一个波&#xff0c;会发生衍…

chrome源码中WeakPtr 跨线程使用详解:原理、风险与最佳实践

base::WeakPtr 在 Chromium 中 不能安全地跨线程使用。这是一个很关键的点&#xff0c;下面详细解释原因及正确用法。 &#x1f50d;原理与使用 ✅ 先说答案&#xff1a; base::WeakPtr 本质上是**线程绑定&#xff08;thread-affine&#xff09;**的。不能在多个线程之间创建…

hysAnalyser 从MPEG-TS导出ES功能说明

摘要 hysAnalyser 是一款特色的 MPEG-TS 数据分析工具。本文主要介绍了 hysAnalyser 从MPEG-TS 中导出选定的 ES 或 PES 功能(版本v1.0.003)&#xff0c;以便用户知悉和掌握这些功能&#xff0c;帮助分析和解决各种遇到ES或PES相关的实际问题。hysAnalyser 支持主流的MP1/MP2/…

C++(21):fstream的读取和写入

目录 1 ios::out 2 ios::in和is_open 3 put()方法 4 get()方法 4.1 读取单个字符 4.2 读取多个字符 4.3 设置终结符 5 getline() 1 ios::out 打开文件用于写入数据。如果文件不存在&#xff0c;则新建该文件&#xff1b;如果文件原来就存在&#xff0c;则打开时清除…