JWT:你真的了解它吗?

大家好,我是石头~

在数字化时代,网络安全和用户隐私保护成为了我们无法忽视的关键议题,也是我们作为一个后端开发的必修课。

而在这个领域中,JWT(JSON Web Token)作为一种现代、安全且高效的会话管理机制,在各类Web服务及API接口中得到了广泛应用。

那么,什么是JWT?

1、初识JWT

JWT,全称为JSON Web Token,是一种开放标准(RFC 7519),用于在网络应用环境间安全地传输信息。

它本质上是一个经过数字签名的JSON对象,能够携带并传递状态信息(如用户身份验证、授权等)。

了解了JWT之后,那么它的组成结构又是怎样的?

2、JWT的结构

如上图,JWT由三部分组成,通过点号(.)连接,这三部分分别是头部(Header)、载荷(Payload)和签名(Signature)。

  • 头部(Header):声明了JWT的类型(通常是JWT)以及所使用的加密算法(例如HMAC SHA256或RSA)

  • 载荷(Payload):承载实际数据的部分,可以包含预定义的声明(如iss(签发者)、exp(过期时间)、sub(主题)等)以及其它自定义的数据。这些信息都是铭文的,但不建议存放敏感信息。

  • 签名(Signature):通过对前两部分进行编码后的信息,使用指定的密钥通过头部(Header)中声明的加密算法生成,拥有验证数据完整性和防止篡改。

3、JWT的常规认证流程

JWT的认证流程如上图。当用户登录时,服务器通过验证用户名和密码后,会生成一个JWT,并将其发送给客户端。这个JWT中可能包含用户的认证信息、权限信息以及其它必要的业务数据。

客户端在接收到JWT后,通常将其保存在本地(如Cookie、LocalStorage或者SessionStorage)。

客户端在后续的请求中,携带此JWT(通常是附在HTTP请求头中),无需再次提交用户名和密码。服务器只需对收到的JWT进行解码并验证签名,即可完成用户身份的确认和权限验证。

4、JWT的完整认证流程

在上面的JWT常规认证流程中,我们可以正常完成登陆、鉴权等认证,但是你会发现在这个流程中,我们无法实现退出登陆。

当服务端将JWT发放给客户端后,服务端就失去了对JWT的控制权,只能等待这些发放出去的JWT超过有效期,自然失效。

为了解决这个问题,我们引入了缓存,如下图。

当服务端生成JWT之后,在返回给客户端之前,先将JWT存入缓存中。要鉴权的时候,需要检验缓存中是否存在这个JWT。

这样的话,如果用户退出登陆,我们只需要将缓存中的JWT删除,即可保证发放出去的JWT无法再通过鉴权。

5、JWT的优势与挑战

JWT的主要优点在于无状态性,服务器无需存储会话状态,减轻了服务器压力,同时提高了系统的可扩展性和性能。

此外,由于JWT的有效期限制,增强了安全性。

然而,JWT也面临一些挑战,比如密钥的安全保管、JWT过期策略的设计以及如何处理丢失或被盗用的情况。

因此,在实际应用中,需要综合考虑业务场景和技术特性来合理运用JWT。

6、JWT示例

概念讲完了,我们最后来看个实例吧。

typescript 复制代码
// Java代码示例
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

// 假设有一个User类,其中包含用户ID
public class User {
    private String id;
    // 其他属性和方法...
}

// 创建JWT
public String generateJWT(User user) {
    // 设置秘钥(在此处使用的是HMAC SHA-256算法)
    String secret = "your-secret-key"; // 在实际场景中应当从安全的地方获取秘钥
    long ttlMillis = 60 * 60 * 1000; // JWT的有效期为1小时

    // 构建载荷,包含用户ID和其他相关信息
    Map<String, Object> claims = new HashMap<>();
    claims.put("userId", user.getId());
    claims.put("exp", System.currentTimeMillis() + ttlMillis); // 设置过期时间

    // 生成JWT
    String jwt = Jwts.builder()
            .setClaims(claims)
            .signWith(SignatureAlgorithm.HS256, secret.getBytes(StandardCharsets.UTF_8))
            .compact();
    // TODO JWT写入缓存
    return jwt;
}

// 验证JWT
public boolean validateJWT(String jwtToken, String secretKey) {
    boolean flag = false;
    try {
        Jwts.parser().setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8)).parseClaimsJws(jwtToken);
        // 如果没有抛出异常,则JWT验证通过
        flag = true;
    } catch (ExpiredJwtException e) {
        // 如果Token过期
        System.out.println("JWT已过期");
    } catch (UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) {
        // 其他非法情况,如格式错误、签名无效等
        System.out.println("JWT验证失败:" + e.getMessage());
    }
    if (flag) {
        // TODO 校验缓存中是否有该JWT
    }
    return false;
}

// 使用示例
public static void main(String[] args) {
    User user = new User("123"); // 假设这是合法用户的ID
    String token = generateJWT(user); // 生成JWT
    System.out.println("生成的JWT Token: " + token);

    // 验证生成的JWT
    boolean isValid = validateJWT(token, "your-secret-key");
    if (isValid) {
        System.out.println("JWT验证通过!");
    } else {
        System.out.println("JWT验证未通过!");
    }
}

**MORE | 更多精彩文章**

相关推荐
leonardee6 分钟前
【玩转全栈】----Django基本配置和介绍
java·后端
一只小阿乐7 分钟前
react 点击事件注意事项
前端·javascript·react.js·react
Mike_jia14 分钟前
EMQX:开源MQTT消息中间件王者,百万级物联网连接的首选引擎
前端
xiaoxue..18 分钟前
深入理解JavaScript中的深拷贝与浅拷贝:内存管理的艺术
开发语言·前端·javascript·面试
绝无仅有19 分钟前
电商大厂面试题解答与场景解析(二)
后端·面试·架构
Mapmost20 分钟前
【高斯泼溅】深度解析Three.js 加载3D Gaussian Splatting模型
前端
绝无仅有20 分钟前
某电商大厂场景面试相关的技术文章
后端·面试·架构
李昊哲小课30 分钟前
手写 Spring Boot 嵌入式Tomcat项目开发教学
spring boot·后端·tomcat
Jeled35 分钟前
RecyclerView ViewHolder 复用机制详解(含常见错乱问题与优化方案)
android·学习·面试·kotlin
鹏多多41 分钟前
详解React组件状态管理useState
前端·javascript·react.js