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

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.xdnf.cn/news/16758.html

相关文章:

  • Agents-SDK智能体开发[2]之工具调用
  • Web开发-PHP应用TP框架MVC模型路由访问模版渲染安全写法版本漏洞
  • Mysql group by
  • 机器学习第二课之逻辑回归(二)LogisticRegression
  • 链表【各种题型+对应LeetCode习题练习】
  • 力扣-最大单词长度乘积
  • macOS卸载.net core 8.0
  • PL-0功能拓展及基于VSCode的IDE配置
  • uniapp无线(WIFI)运行调试APP(真机)
  • 脚本统计MongoDB集合表数据量
  • SSL 剥离漏洞
  • Java 日期时间处理:分类、用途与性能分析
  • 在Centos7中安装gitlab
  • 常见小目标检测算法优化
  • 机器学习:开启智能时代的钥匙
  • TypeScript SDK 升级:通过 Upload Relay 赋能更多应用
  • 第十四天:C++内存管理
  • 汽车供应链PPAP自动化审核指南:如何用AI实现规则精准匹配与文件智能校验
  • cesium FBO(四)自定义相机渲染到Canvas(离屏渲染)
  • 数据处理四件套:NumPy/Pandas/Matplotlib/Seaborn速通指南
  • ACM SIGCOMM 2024论文精选-01:5G【Prism5G】
  • Docker设置容器时间
  • 深度学习(鱼书)day08--误差反向传播(后三节)
  • PyTorch基础——张量计算
  • datax-cloud 智数通修订版(去除mapstruct)本地部署-记录
  • C语言-字符串(定义)、字符串函数(strlen、strcat、strcpy、strcmp、strlwr、strupr)
  • [CISCN 2022 初赛]online_crt
  • 【支持Ubuntu22】Ambari3.0.0+Bigtop3.2.0——Step7—Mariadb初始化
  • 【兆易创新】单片机GD32F103C8T6系列入门资料
  • xcode swift项目运行、连接真机运行报错,引入文件夹失败