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

Java:JWT 从原理到高频面试题解析

目录

一、什么是 JWT?

二、JWT 的结构

1. Header(头部)

2. Payload(载荷)

3. Signature(签名)

三、Java 中实现 JWT(基于 JJWT 库)

1. 引入依赖(Maven)

2. JWT 工具类实现

四、JWT 在 Java Web 中的应用流程

五、JWT 的优缺点及注意事项

优点:

缺点:

注意事项:

六、Java 秋招 JWT 高频面试题

1. JWT 由哪几部分组成?各部分作用是什么?

2. JWT 和 Session 认证的区别是什么?

3. 如何处理 JWT 令牌过期问题?

4. JWT 的 Payload 可以存储敏感信息吗?为什么?

5. JWT 的签名算法 HS256 和 RS256 有什么区别?

6. 如何防止 JWT 令牌被篡改?

7. JWT 的缺点是什么?如何规避?

总结


一、什么是 JWT?

JWT(JSON Web Token)是一种基于 JSON 的轻量级令牌格式,用于在网络应用间安全传递声明信息。它通过数字签名保证信息的完整性和真实性,常用于身份认证、授权和信息交换场景。

与传统的 Session 认证相比,JWT 具有无状态特性:服务器无需存储会话信息,只需通过令牌本身即可验证用户身份,非常适合分布式系统和微服务架构。

二、JWT 的结构

JWT 由三部分组成,用英文句号(.)分隔,格式为:Header.Payload.Signature,三部分均通过 Base64URL 编码(便于在 URL 中传输)。

1. Header(头部)

声明令牌类型(typ: "JWT")和签名算法(如HS256RS256等)。

{"alg": "HS256",  // 签名算法:HMAC SHA-256"typ": "JWT"     // 令牌类型
}

2. Payload(载荷)

存储需要传递的声明信息(Claims),分为三种类型:

  • 注册声明(标准字段,可选):iss(签发者)、exp(过期时间,时间戳)、sub(主题)、iat(签发时间)等。
  • 公共声明:自定义字段(需避免与标准字段冲突)。
  • 私有声明:服务端与客户端协商的自定义业务字段(如用户 ID、角色)。
{"iss": "backend-server",  // 签发者"exp": 1690848000,        // 过期时间(2023-08-01 00:00:00)"sub": "user-auth",       // 主题"userId": 1001,           // 私有声明:用户ID"role": "admin"           // 私有声明:用户角色
}

⚠️ 注意:Payload 仅经过 Base64 编码(可解码),不加密,因此禁止存储敏感信息(如密码)

3. Signature(签名)

JWT 的安全核心,用于验证令牌是否被篡改。生成规则:

  1. 用 Header 中指定的算法,将编码后的 Header、编码后的 Payload,以及服务器端密钥(secret)进行加密。
  2. 公式(以 HS256 为例):

    plaintext

    HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret
    )
    

三、Java 中实现 JWT(基于 JJWT 库)

JJWT(Java JWT)是 Java 生态中常用的 JWT 处理库,以下是完整实现示例。

1. 引入依赖(Maven)

<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 io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;public class JwtUtils {// 密钥(实际开发中需从配置文件读取,且确保足够复杂)private static final String SECRET_KEY = "your-256-bit-secret-key-which-should-be-very-long-and-secure";// 令牌过期时间(3600秒 = 1小时)private static final long EXPIRATION_TIME = 3600 * 1000;// 生成密钥(基于HS256算法,需要256位密钥)private static SecretKey getSigningKey() {byte[] keyBytes = SECRET_KEY.getBytes();return Keys.hmacShaKeyFor(keyBytes);}// 生成JWT令牌public static String generateToken(String username, Map<String, Object> claims) {Date now = new Date();Date expirationDate = new Date(now.getTime() + EXPIRATION_TIME);return Jwts.builder().setClaims(claims)                  // 自定义声明.setSubject(username)               // 主题(如用户名).setIssuedAt(now)                   // 签发时间.setExpiration(expirationDate)      // 过期时间.signWith(getSigningKey(), SignatureAlgorithm.HS256)  // 签名.compact();}// 从令牌中获取用户名(主题)public static String getUsernameFromToken(String token) {return getClaimFromToken(token, Claims::getSubject);}// 验证令牌是否有效(未过期且签名正确)public static boolean validateToken(String token, String username) {String tokenUsername = getUsernameFromToken(token);return (username.equals(tokenUsername) && !isTokenExpired(token));}// 从令牌中获取指定声明private static <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {Claims claims = getAllClaimsFromToken(token);return claimsResolver.apply(claims);}// 解析令牌获取所有声明private static Claims getAllClaimsFromToken(String token) {return Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();}// 判断令牌是否过期private static boolean isTokenExpired(String token) {Date expiration = getClaimFromToken(token, Claims::getExpiration);return expiration.before(new Date());}// 测试方法public static void main(String[] args) {// 自定义声明(如用户角色、ID)Map<String, Object> claims = new HashMap<>();claims.put("userId", 1001);claims.put("role", "admin");// 生成令牌String token = generateToken("zhangsan", claims);System.out.println("生成的JWT令牌:" + token);// 解析令牌String username = getUsernameFromToken(token);Integer userId = getClaimFromToken(token, claims1 -> claims1.get("userId", Integer.class));System.out.println("解析结果:用户名=" + username + ",userId=" + userId);// 验证令牌boolean isValid = validateToken(token, "zhangsan");System.out.println("令牌是否有效:" + isValid);}
}

四、JWT 在 Java Web 中的应用流程

以 Spring Boot 身份认证为例:

  1. 用户登录

    • 客户端提交用户名和密码。
    • 服务端验证通过后,调用JwtUtils.generateToken()生成 JWT。
    • 服务端将 JWT 返回给客户端(客户端通常存储在localStorageCookie中)。
  2. 客户端请求资源

    • 客户端在 HTTP 请求头中携带 JWT:Authorization: Bearer <token>
  3. 服务端验证

    • 服务端通过拦截器 / 过滤器提取请求头中的 JWT。
    • 调用JwtUtils.validateToken()验证令牌有效性。
    • 验证通过则允许访问,否则返回 401(未授权)。

五、JWT 的优缺点及注意事项

优点:

  • 无状态:服务器无需存储会话,减轻服务器压力,适合分布式系统。
  • 自包含:Payload 可携带用户信息,减少数据库查询。
  • 跨域支持:可在不同域名间传递,适合前后端分离架构。

缺点:

  • 无法作废已颁发的令牌:一旦生成,在过期前始终有效(除非服务端维护黑名单)。
  • Payload 无加密:敏感信息不能存储在 Payload 中。

注意事项:

  1. 密钥必须安全存储(如配置中心),避免泄露。
  2. 令牌过期时间不宜过长(建议 1 小时内),降低被盗用风险。
  3. 传输需通过 HTTPS,防止令牌被拦截。
  4. 避免在 Payload 中存储敏感信息(如密码、token 等)。

六、Java 秋招 JWT 高频面试题

1. JWT 由哪几部分组成?各部分作用是什么?

答:JWT 由 Header、Payload、Signature 三部分组成。

  • Header:声明令牌类型和签名算法。
  • Payload:存储声明信息(如用户 ID、过期时间),仅 Base64 编码,不加密。
  • Signature:通过密钥和算法对前两部分签名,用于验证令牌完整性和真实性。

2. JWT 和 Session 认证的区别是什么?

答:

  • 存储位置:Session 存储在服务端,JWT 存储在客户端。
  • 状态性:Session 是有状态的(服务端需维护会话),JWT 是无状态的(服务端无需存储)。
  • 分布式支持:Session 需考虑共享(如 Redis),JWT 天然支持分布式。
  • 安全性:Session 依赖 Cookie,可能遭遇 CSRF 攻击;JWT 需注意密钥安全和 HTTPS 传输。

3. 如何处理 JWT 令牌过期问题?

答:

  • 方案 1:设置合理的过期时间(如 1 小时),过期后要求用户重新登录。
  • 方案 2:使用 “双令牌机制”:
    • 访问令牌(Access Token):短期有效(如 30 分钟),用于接口访问。
    • 刷新令牌(Refresh Token):长期有效(如 7 天),用于过期后重新获取访问令牌。

4. JWT 的 Payload 可以存储敏感信息吗?为什么?

答:不可以。因为 Payload 仅经过 Base64 编码(可解码为明文),未加密,敏感信息(如密码、银行卡号)会被泄露。

5. JWT 的签名算法 HS256 和 RS256 有什么区别?

答:

  • HS256:对称加密算法,签名和验证使用同一个密钥,需确保密钥在服务端安全存储。
  • RS256:非对称加密算法,使用私钥签名、公钥验证,适合多服务间共享验证(只需分发公钥)。

6. 如何防止 JWT 令牌被篡改?

答:通过 Signature 部分保证。服务端生成令牌时用密钥签名,验证时重新计算签名并与令牌中的 Signature 比对,若不一致则说明令牌被篡改。

7. JWT 的缺点是什么?如何规避?

答:

  • 缺点 1:令牌生成后无法主动作废(除非过期)。
    规避:维护令牌黑名单(如 Redis 存储已注销的令牌),验证时检查黑名单。
  • 缺点 2:Payload 无加密,敏感信息易泄露。
    规避:不在 Payload 中存储敏感信息,必要时对 Payload 单独加密。

总结

JWT 是 Java 开发中处理身份认证和信息传递的重要工具,其无状态特性使其在分布式系统中极具优势。掌握 JWT 的原理、实现及安全注意事项,不仅能提升项目开发能力,也是秋招面试中的高频考点。实际开发中需结合业务场景合理使用,平衡安全性和便利性。

http://www.dtcms.com/a/310580.html

相关文章:

  • 04 基于sklearn的机械学习-梯度下降(上)
  • 什么样的业务需要国内动态IP轮换?
  • 第二篇:Linux 文件系统操作:从基础到进阶
  • RAL-2025 | “藏宝图”驱动的具身导航!HAM-Nav:基于手绘地图引导的机器人导航
  • GitPython08-源码解读
  • 进阶08:C#与SQL Server通信
  • 高效连接,3针M12航空插头助您畅行无阻
  • PSA 制氧装置和VPSA 制氧装置技术特点有什么不同
  • [VL|RIS] ReferSAM
  • windows电脑开机或重启,server不能自启动
  • 关税战火中的技术方舟:新西兰证券交易所的破局之道 ——从15%关税冲击到跨塔斯曼结算联盟,解码下一代交易基础设施
  • 开发后台管理系统的注意事项
  • 26考研|数学分析:曲线(面)积分·三大公式
  • Windows系统优化命令-记录
  • 全国增值税发票查验流程-批量核验-接口集成简便高效
  • 四、基于SpringBoot,MVC后端开发笔记
  • opencv-python的GPU调用
  • 在线免费的AI文本转语音工具TTSMaker介绍
  • U-Mail邮件系统-全面适配信创环境的国产邮件系统
  • 什么是大端?什么是小端?如何验证?
  • MySQL相关概念和易错知识点(3)(表内容的CURD、内置函数)
  • 基于CNN卷积神经网络图像识别28个识别合集-视频介绍下自取
  • Tushare 行情数据完整性同步算法
  • 三轴云台之热成像伪彩模式篇
  • 【Lua】题目小练7
  • Nestjs框架: 请求生命周期与应用生命周期
  • Vue模板语法详解:从基础到进阶的响应式绑定指南1
  • 工业数采引擎-DTU
  • CSS属性值计算规则:从声明到渲染的精确过程
  • 《C++》STL--list容器详解