当前位置: 首页 > news >正文

JWT 是由哪三个部分组成?如何使用JWT进行身份认证?

JWT 是由哪三个部分组成,请写出对应的组成和作用。

JWT(JSON Web Token)是一种轻量级的身份认证令牌,核心由 3个Base64编码的JSON部分 组成,三部分用英文句号(.)连接,格式为 Header.Payload.Signature

每个部分的组成、作用如下:

1. 第一部分:Header(头部)

组成
  • 是一个JSON对象,包含两个核心字段:
    • alg:指定签名算法(如 HS256 哈希算法、RS256 非对称加密算法),必填;
    • typ:指定令牌类型,固定为 JWT,可选(默认即可)。
  • 示例(原始JSON):
    {"alg": "HS256","typ": "JWT"
    }
    
  • 最终会经过 Base64Url编码 形成令牌的第一部分(注:Base64Url是Base64的变体,适配URL传输,替换了+//字符)。
作用
  • 告诉接收方(如后端服务器):该JWT使用的签名算法是什么,以及令牌类型是JWT,方便接收方解码和验证。

2. 第二部分:Payload(负载/载荷)

组成
  • 是一个JSON对象,包含需要传递的核心数据(也叫Claims,“声明”),分为3类:
    • 标准声明(可选,约定俗成的通用字段):
      • iss:令牌签发者(如“xxx系统”);
      • exp:令牌过期时间(时间戳,如1735689600,必填,防止令牌永久有效);
      • iat:令牌签发时间(时间戳);
      • sub:令牌面向的用户(如用户ID);
      • aud:令牌的接收方(如“xxx接口服务”)。
    • 自定义声明(业务字段):
      • 按需添加的业务数据,如 userId: 1001role: "admin"username: "zhangsan"
  • 示例(原始JSON):
    {"iss": "user-auth-system","exp": 1735689600,"userId": 1001,"role": "admin"
    }
    
  • 同样经过 Base64Url编码 形成令牌的第二部分。
作用
  • 承载身份信息、权限信息或业务数据,实现“无状态认证”——后端无需存储会话,只需解码Payload即可获取用户身份,减少服务器存储压力。
  • 注意:Payload是明文编码(Base64Url可反向解码),不能存储敏感数据(如密码、银行卡号)!

3. 第三部分:Signature(签名)

组成
  • 不是JSON对象,而是通过 Header指定的算法,对“编码后的Header + 编码后的Payload + 密钥(Secret)”进行加密计算得到的字符串。
  • 计算逻辑(以HS256算法为例):
    Signature = HMACSHA256(Base64UrlEncode(Header) + "." + Base64UrlEncode(Payload),密钥(如"xxx-2024-secret-key")
    )
    
  • 最终结果是一串不可反向解密的字符串,作为令牌的第三部分。
作用
  • 核心是防止令牌被篡改:接收方(后端)收到JWT后,会用同样的逻辑(相同的Header算法、相同的密钥)重新计算签名,若计算结果与令牌的Signature一致,则说明Header和Payload未被篡改,令牌有效;若不一致,则直接拒绝。
  • 密钥的安全性至关重要:对称算法(如HS256)的密钥需仅在签发方和验证方保存;非对称算法(如RS256)用私钥签发、公钥验证,更适合分布式系统。

完整JWT示例(直观理解)

假设上述Header、Payload编码后,加上Signature,最终的JWT令牌如下(三部分用.分隔):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ1c2VyLWF1dGgtc3lzdGVtIiwiZXhwIjoxNzM1Njg5NjAwLCJ1c2VySWQiOjEwMDEsInJvbGUiOiJhZG1pbiJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

关键总结

  • 3部分核心逻辑:Header(算法+类型)→ Payload(数据)→ Signature(防篡改);
  • 核心价值:无状态、轻量、可跨服务传递身份信息,适合前后端分离、微服务架构的身份认证;
  • 安全提醒:Payload明文,不存敏感数据;Signature依赖密钥,密钥绝不能泄露。

如何使用JWT进行身份认证?

使用 JWT 进行身份认证的核心流程是 “签发令牌→携带令牌→验证令牌”,全程无状态(后端不存储会话),适配前后端分离、微服务架构,具体步骤和实操细节如下:

一、核心认证流程(3步走)

1. 第一步:用户登录,后端签发 JWT 令牌

这是唯一需要验证用户账号密码的步骤,验证通过后生成令牌返回给前端。

  • 流程细节

    1. 前端提交用户凭证(如 username + password)到后端登录接口(如 /login);
    2. 后端验证凭证(查询数据库对比账号密码,或对接OAuth2.0等第三方认证);
    3. 验证通过后,构建 JWT 的 Header(指定算法)Payload(用户ID、角色、过期时间等非敏感数据)
    4. 用后端保存的 密钥(Secret),按 Header 指定的算法生成 Signature,拼接 Header.Payload.Signature 得到完整 JWT;
    5. 后端将 JWT 令牌返回给前端(通常放在响应体 { "token": "xxx.jwt" })。
  • 实操示例(Java + Spring Boot)
    依赖 JWT 工具包(如 io.jsonwebtoken:jjwt),编写签发逻辑:

    // JWT 工具类(核心方法:生成令牌)
    public class JwtUtil {// 密钥(必须保密,生产环境用配置文件存储,避免硬编码)private static final String SECRET = "your-strong-secret-key-32bytes+";// 令牌过期时间(如 2 小时,单位:毫秒)private static final long EXPIRATION = 7200000;// 生成 JWT 令牌public static String generateToken(User user) {// 1. 构建 Payload(包含标准声明和自定义业务数据)Map<String, Object> claims = new HashMap<>();claims.put("userId", user.getId()); // 自定义:用户IDclaims.put("role", user.getRole()); // 自定义:用户角色return Jwts.builder().setClaims(claims) // 载荷数据.setIssuedAt(new Date()) // 签发时间.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) // 过期时间.signWith(SignatureAlgorithm.HS256, SECRET) // 签名算法 + 密钥.compact(); // 生成最终令牌}
    }// 登录接口(Controller)
    @PostMapping("/login")
    public Result login(@RequestBody LoginDTO loginDTO) {// 1. 验证账号密码(示例:从数据库查询用户)User user = userService.verify(loginDTO.getUsername(), loginDTO.getPassword());if (user == null) {return Result.error("账号或密码错误");}// 2. 生成 JWT 令牌String token = JwtUtil.generateToken(user);// 3. 返回令牌给前端return Result.success("登录成功", Collections.singletonMap("token", token));
    }
    
2. 第二步:前端存储并携带 JWT 令牌

前端拿到令牌后,需在后续请求中携带,让后端识别用户身份。

  • 存储方式

    • 短期存储:localStorage(持久化,关闭浏览器不丢失)或 sessionStorage(会话级,关闭浏览器失效);
    • 注意:避免存储在 cookie 中(易受 CSRF 攻击,除非开启 HttpOnlySameSite)。
  • 携带方式
    前端每次请求后端接口(如查询用户信息、提交订单)时,将 JWT 放在 HTTP 请求头的 Authorization 字段 中(行业标准),格式为:

    Authorization: Bearer {你的JWT令牌}
    

    (注:Bearer 后有一个空格,不可省略)

  • 实操示例(前端 Vue/React)

    // 1. 登录成功后存储令牌(Vue示例)
    login() {axios.post("/login", { username: "zhangsan", password: "123456" }).then(res => {const token = res.data.data.token;localStorage.setItem("jwtToken", token); // 存储到localStorage});
    }// 2. 全局请求拦截器(自动携带令牌)
    axios.interceptors.request.use(config => {const token = localStorage.getItem("jwtToken");if (token) {config.headers.Authorization = `Bearer ${token}`; // 添加请求头}return config;
    });
    
3. 第三步:后端验证 JWT 令牌,授权访问

后端对需要身份认证的接口,统一拦截并验证 JWT 合法性,验证通过则放行,否则拒绝请求。

  • 验证核心逻辑

    1. 从请求头 Authorization 中提取 JWT 令牌(去掉 Bearer 前缀);
    2. 验证令牌有效性:
      • 签名是否合法(用相同密钥和算法重新计算签名,对比是否一致,防止篡改);
      • 令牌是否过期(检查 exp 字段的时间戳);
      • 可选验证:签发者(iss)、接收方(aud)是否匹配(防止令牌被滥用);
    3. 验证通过后,从 Payload 中提取用户信息(如 userIdrole),存入上下文(如 ThreadLocal);
    4. 后续业务逻辑可直接从上下文获取用户信息(如查询当前用户的订单)。
  • 实操示例(Java + Spring Boot 拦截器)
    用 Spring 的 HandlerInterceptorFilter 实现全局令牌验证:

    // 1. JWT 验证拦截器
    public class JwtAuthInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// (1)提取令牌:从 Authorization 头获取String authHeader = request.getHeader("Authorization");if (authHeader == null || !authHeader.startsWith("Bearer ")) {// 无令牌,返回401未授权response.setStatus(HttpStatus.UNAUTHORIZED.value());response.getWriter().write("请先登录");return false;}String token = authHeader.substring(7); // 去掉 "Bearer " 前缀try {// (2)验证令牌有效性(签名 + 过期时间)Claims claims = Jwts.parser().setSigningKey(JwtUtil.SECRET) // 用相同密钥验证.parseClaimsJws(token) // 解析令牌.getBody(); // 获取Payload数据// (3)将用户信息存入上下文(供后续接口使用)Long userId = claims.get("userId", Long.class);String role = claims.get("role", String.class);UserContext.setUserId(userId); // ThreadLocal存储UserContext.setRole(role);return true; // 验证通过,放行} catch (ExpiredJwtException e) {// 令牌过期response.setStatus(HttpStatus.UNAUTHORIZED.value());response.getWriter().write("令牌已过期,请重新登录");} catch (SignatureException e) {// 签名非法(令牌被篡改)response.setStatus(HttpStatus.FORBIDDEN.value());response.getWriter().write("令牌无效");} catch (Exception e) {response.setStatus(HttpStatus.UNAUTHORIZED.value());response.getWriter().write("身份认证失败");}return false;}// 接口执行完后清理ThreadLocal(避免内存泄漏)@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserContext.clear();}
    }// 2. 注册拦截器(Spring Boot配置)
    @Configuration
    public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new JwtAuthInterceptor()).addPathPatterns("/**") // 所有接口都拦截.excludePathPatterns("/login") // 排除登录接口(无需认证).excludePathPatterns("/register") // 排除注册接口.excludePathPatterns("/static/**"); // 排除静态资源}
    }// 3. 业务接口(直接从上下文获取用户信息)
    @GetMapping("/user/info")
    public Result getUserInfo() {Long userId = UserContext.getUserId(); // 无需从请求参数获取,直接从上下文拿User user = userService.getById(userId);return Result.success(user);
    }
    

二、关键优化:解决 JWT 痛点

JWT 本身是“一旦签发无法撤回”的,需通过以下方式解决核心痛点:

1. 令牌过期与刷新机制
  • 问题:令牌过期后用户需重新登录,体验差;
  • 方案:引入 访问令牌(Access Token)+ 刷新令牌(Refresh Token)
    • 访问令牌:短期有效(如 2 小时),用于接口访问,过期快,风险低;
    • 刷新令牌:长期有效(如 7 天),仅用于获取新的访问令牌,存储在更安全的地方(如 HttpOnly Cookie);
    • 流程:访问令牌过期时,前端用刷新令牌调用 /refresh-token 接口,后端验证刷新令牌有效后,返回新的访问令牌,用户无需重新登录。
2. 令牌黑名单(应对注销/账号冻结)
  • 问题:用户注销或账号被冻结后,已签发的未过期令牌仍能使用;
  • 方案:维护“令牌黑名单”(存储已注销/失效的令牌):
    • 用 Redis 存储黑名单,key 为令牌,value 为过期时间(与 JWT 的 exp 一致);
    • 验证令牌时,先检查是否在黑名单中,若在则直接拒绝;
    • 优势:Redis 高性能,支持过期自动删除,不占用大量内存。
3. 安全加固
  • 密钥安全:生产环境用长密钥(如 32 字节以上),存储在配置中心(如 Nacos、Apollo),避免硬编码;
  • 算法选择:分布式系统用非对称算法(如 RS256),私钥签发令牌,公钥验证,避免密钥泄露风险;
  • Payload 安全:绝不存储敏感数据(密码、银行卡号),仅存非敏感身份信息(用户ID、角色);
  • HTTPS 传输:所有请求用 HTTPS,防止令牌被中间人窃取。

三、适用场景与不适用场景

适用场景:
  • 前后端分离架构(Vue/React + Spring Boot);
  • 微服务架构(跨服务身份认证,无需共享会话);
  • 无状态服务(降低服务器存储压力,便于水平扩展)。
不适用场景:
  • 需即时撤回令牌的场景(如银行转账,需实时禁用账号);
  • 敏感数据传输(Payload 可解码,无法加密);
  • 会话需关联复杂状态的场景(如购物车未登录状态保持)。

总结

JWT 身份认证的核心是“无状态、轻量级、跨服务”,核心流程可概括为:

  1. 登录 → 后端验证凭证 → 签发 JWT;
  2. 后续请求 → 前端携带 JWT;
  3. 后端拦截 → 验证 JWT → 授权访问。

通过“刷新令牌+黑名单+HTTPS”可解决其核心痛点,是目前前后端分离、微服务架构的首选身份认证方案。

JWT的Payload部分是否可以包含敏感信息?

不可以,JWT 的 Payload 部分绝对不能包含密码、银行卡号、手机号等敏感信息。

核心原因

  1. Payload 是明文编码而非加密:Payload 仅通过 Base64Url 编码(不是加密算法)处理,任何人获取到 JWT 令牌后,都能通过 Base64Url 解码工具直接还原出原始数据,无任何保密性可言。
  2. 令牌传输易泄露:JWT 通常通过 HTTP 请求头、localStorage 等方式传输和存储,可能被中间人窃取或通过前端漏洞获取,敏感信息会直接暴露。

示例验证

假设 Payload 包含手机号 13800138000,原始 JSON 如下:

{ "userId": 1001, "phone": "13800138000" }

Base64Url 编码后为 eyJ1c2VySWQiOjEwMDEsInBob25lIjoiMTM4MDAxMzgwMDAifQ,通过在线解码工具可直接还原出手机号,毫无隐私保护。

正确做法

  1. Payload 仅存非敏感身份/权限信息:如用户 ID、角色、用户名(非隐私字段)、令牌过期时间等,用于后端识别用户身份和授权。
  2. 敏感信息存储在后端:敏感数据仅保存在数据库或安全的缓存(如加密后的 Redis)中,后端通过 Payload 中的用户 ID 等非敏感信息,查询获取敏感数据。
  3. 需传输敏感数据时单独加密:若业务必须传输敏感信息,需额外通过 AES 等加密算法加密后,再作为业务参数传递(不放入 Payload)。
http://www.dtcms.com/a/560677.html

相关文章:

  • 【JUnit实战3_24】 第十四章:JUnit 5 扩展模型(Extension API)实战(下)
  • PostgreSQL pg_stat_bgwriter 视图各个字段详解
  • 简单的购物网站设计网页设计尺寸pc端
  • Unity 高效 ListView GridView
  • 【3DV 进阶-4】VecSet 论文+代码对照理解
  • Oracle实用参考(13)——Oracle for Linux (RAC)到Oracle for Linux(单实例)间OGG单向复制环境搭建(2)
  • 前端开发 网站建设头像logo图片在线制作免费
  • 电话语音接入扣子介绍
  • Go分布式追踪实战:从理论到OpenTelemetry集成|Go语言进阶(15)
  • Vue-理解 vuex
  • 【Android】View滑动的实现
  • 广西南宁网站优化急切网头像在线制作图片
  • 创建对象中的单例模式
  • AI革新汽车安全软件开发
  • 单例模式并使用多线程方式验证
  • 小梦音乐下载器(高品质MP3下载) 中文绿色版
  • 网站群发推广软件wordpress页面显示文章
  • Redis大Key调优指针
  • Redis BigKey场景实战
  • Vue消息订阅与发布
  • 12306网站建设超30亿个人网站做贷款广告
  • 《Streamlit 交互式 Web 应用开发》总结测试题
  • 大连 网站制作黑龙江做网站
  • ASP.NET Core 9 Web Api 启用 Swagger
  • Web APIs学习第三天:事件
  • UVa 1597 Searching the Web
  • 5分钟读懂MySQL+Redis双写一致性实现流程
  • 从零开始构建PDF文档生成器(二)- 添加页眉页脚
  • PostgreSQL 中 pg_stat_database 视图的 tup_returned 字段详解
  • 网络原理--HTTP