JWT 是由哪三个部分组成,请写出对应的组成和作用。
JWT(JSON Web Token)是一种轻量级的身份认证令牌,核心由 3个Base64编码的JSON部分 组成,三部分用英文句号(.)连接,格式为 Header.Payload.Signature。
每个部分的组成、作用如下:
1. 第一部分:Header(头部)
组成
- 
是一个JSON对象,包含两个核心字段:
alg:指定签名算法(如HS256哈希算法、RS256非对称加密算法),必填;typ:指定令牌类型,固定为JWT,可选(默认即可)。
 - 
示例(原始JSON):
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: 1001、role: "admin"、username: "zhangsan"。 
 - 按需添加的业务数据,如 
 
 - 标准声明(可选,约定俗成的通用字段):
 - 
示例(原始JSON):
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 令牌
这是唯一需要验证用户账号密码的步骤,验证通过后生成令牌返回给前端。
- 
流程细节:
- 前端提交用户凭证(如 
username+password)到后端登录接口(如/login); - 后端验证凭证(查询数据库对比账号密码,或对接OAuth2.0等第三方认证);
 - 验证通过后,构建 JWT 的 Header(指定算法) 和 Payload(用户ID、角色、过期时间等非敏感数据);
 - 用后端保存的 密钥(Secret) ,按 Header 指定的算法生成 Signature ,拼接 
Header.Payload.Signature得到完整 JWT; - 后端将 JWT 令牌返回给前端(通常放在响应体 
{ "token": "xxx.jwt" })。 
 - 前端提交用户凭证(如 
 - 
实操示例(Java + Spring Boot) :
依赖 JWT 工具包(如
io.jsonwebtoken:jjwt),编写签发逻辑:java// 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()); // 自定义:用户ID claims.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 攻击,除非开启HttpOnly和SameSite)。 
 - 短期存储:
 - 
携带方式 :
前端每次请求后端接口(如查询用户信息、提交订单)时,将 JWT 放在 HTTP 请求头的
Authorization字段 中(行业标准),格式为:httpAuthorization: Bearer {你的JWT令牌}(注:
Bearer后有一个空格,不可省略) - 
实操示例(前端 Vue/React):
javascript// 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 合法性,验证通过则放行,否则拒绝请求。
- 
验证核心逻辑:
- 从请求头 
Authorization中提取 JWT 令牌(去掉Bearer前缀); - 验证令牌有效性:
- 签名是否合法(用相同密钥和算法重新计算签名,对比是否一致,防止篡改);
 - 令牌是否过期(检查 
exp字段的时间戳); - 可选验证:签发者(
iss)、接收方(aud)是否匹配(防止令牌被滥用); 
 - 验证通过后,从 Payload 中提取用户信息(如 
userId、role),存入上下文(如ThreadLocal); - 后续业务逻辑可直接从上下文获取用户信息(如查询当前用户的订单)。
 
 - 从请求头 
 - 
实操示例(Java + Spring Boot 拦截器) :
用 Spring 的
HandlerInterceptor或Filter实现全局令牌验证:java// 1. JWT 验证拦截器 public class JwtAuthInterceptor implements HandlerInterceptor { @Override public 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(避免内存泄漏) @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { UserContext.clear(); } } // 2. 注册拦截器(Spring Boot配置) @Configuration public class WebConfig implements WebMvcConfigurer { @Override public 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 高性能,支持过期自动删除,不占用大量内存。
 
 - 用 Redis 存储黑名单,key 为令牌,value 为过期时间(与 JWT 的 
 
3. 安全加固
- 密钥安全:生产环境用长密钥(如 32 字节以上),存储在配置中心(如 Nacos、Apollo),避免硬编码;
 - 算法选择:分布式系统用非对称算法(如 RS256),私钥签发令牌,公钥验证,避免密钥泄露风险;
 - Payload 安全:绝不存储敏感数据(密码、银行卡号),仅存非敏感身份信息(用户ID、角色);
 - HTTPS 传输:所有请求用 HTTPS,防止令牌被中间人窃取。
 
三、适用场景与不适用场景
适用场景:
- 前后端分离架构(Vue/React + Spring Boot);
 - 微服务架构(跨服务身份认证,无需共享会话);
 - 无状态服务(降低服务器存储压力,便于水平扩展)。
 
不适用场景:
- 需即时撤回令牌的场景(如银行转账,需实时禁用账号);
 - 敏感数据传输(Payload 可解码,无法加密);
 - 会话需关联复杂状态的场景(如购物车未登录状态保持)。
 
总结
JWT 身份认证的核心是"无状态、轻量级、跨服务",核心流程可概括为:
- 登录 → 后端验证凭证 → 签发 JWT;
 - 后续请求 → 前端携带 JWT;
 - 后端拦截 → 验证 JWT → 授权访问。
 
通过"刷新令牌+黑名单+HTTPS"可解决其核心痛点,是目前前后端分离、微服务架构的首选身份认证方案。
JWT的Payload部分是否可以包含敏感信息?
不可以,JWT 的 Payload 部分绝对不能包含密码、银行卡号、手机号等敏感信息。
核心原因
- Payload 是明文编码而非加密:Payload 仅通过 Base64Url 编码(不是加密算法)处理,任何人获取到 JWT 令牌后,都能通过 Base64Url 解码工具直接还原出原始数据,无任何保密性可言。
 - 令牌传输易泄露:JWT 通常通过 HTTP 请求头、localStorage 等方式传输和存储,可能被中间人窃取或通过前端漏洞获取,敏感信息会直接暴露。
 
示例验证
假设 Payload 包含手机号 13800138000,原始 JSON 如下:
            
            
              json
              
              
            
          
          { "userId": 1001, "phone": "13800138000" }
        Base64Url 编码后为 eyJ1c2VySWQiOjEwMDEsInBob25lIjoiMTM4MDAxMzgwMDAifQ,通过在线解码工具可直接还原出手机号,毫无隐私保护。
正确做法
- Payload 仅存非敏感身份/权限信息:如用户 ID、角色、用户名(非隐私字段)、令牌过期时间等,用于后端识别用户身份和授权。
 - 敏感信息存储在后端:敏感数据仅保存在数据库或安全的缓存(如加密后的 Redis)中,后端通过 Payload 中的用户 ID 等非敏感信息,查询获取敏感数据。
 - 需传输敏感数据时单独加密:若业务必须传输敏感信息,需额外通过 AES 等加密算法加密后,再作为业务参数传递(不放入 Payload)。