Java——JWT详细讲解

发布于:2025-05-12 ⋅ 阅读:(24) ⋅ 点赞:(0)

引言

在当代 Web 应用与 API 开发里,用户认证和授权无疑是极为关键的环节。JSON Web Token(JWT)作为一种轻量级的身份验证机制,凭借其简洁性、可扩展性以及跨域支持等显著优势,在众多前后端分离项目中得到了广泛应用。本文将全面深入地探讨 JWT 的原理、工作流程,并且结合 Java 代码示例,展示如何在一个简单的登录系统中运用 JWT 进行认证。

一、JWT 概述

1. 什么是 JWT

JWT 是一种用于在网络应用间安全传递声明的开放标准(RFC 7519)。它通常由三部分构成:头部(Header)、载荷(Payload)和签名(Signature),并以 . 分隔,形成类似 xxxxx.yyyyy.zzzzz 的字符串。

2. JWT 的结构

  • 头部(Header):通常包含两部分信息,令牌的类型(通常是 JWT)和使用的签名算法,如 HMAC SHA256 或 RSA。它是一个 JSON 对象,经过 Base64Url 编码后形成 JWT 的第一部分。
{
  "alg": "HS256",
  "typ": "JWT"
}
  • 载荷(Payload):包含声明(Claims),声明是关于实体(通常是用户)和其他数据的声明。声明分为三种类型:注册声明、公开声明和私有声明。注册声明是一些预定义的声明,如 iss(发行人)、sub(主题)、aud(受众)等。载荷也是一个 JSON 对象,经过 Base64Url 编码后形成 JWT 的第二部分。
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}
  • 签名(Signature):为了创建签名部分,需要使用编码后的头部、编码后的载荷、一个密钥(secret)和头部中指定的签名算法。签名用于验证消息在传递过程中没有被更改,并且在使用私钥签名的情况下,还可以验证 JWT 的发送者的身份。

3. JWT 的优点

  • 无状态:JWT 是无状态的,服务器不需要存储会话信息,因此可以很容易地扩展到多个服务器或微服务架构中。
  • 跨域支持:由于 JWT 可以通过 HTTP 头部或 URL 参数传递,因此可以在不同的域之间安全地使用。
  • 可扩展性:可以在载荷中添加自定义声明,以满足不同的业务需求。

二、JWT 工作流程

1. 用户登录

用户向服务器发送登录请求,提供用户名和密码。

2. 服务器验证

服务器验证用户的用户名和密码,如果验证成功,服务器根据用户信息生成一个 JWT。

3. 返回 JWT

服务器将生成的 JWT 返回给客户端。

4. 客户端存储

客户端接收到 JWT 后,将其存储在本地,通常是在浏览器的 localStoragesessionStorage 中。

5. 后续请求

在后续的请求中,客户端将 JWT 包含在请求的头部(通常是 Authorization 头部)中发送给服务器。

6. 服务器验证 JWT

服务器接收到请求后,验证 JWT 的签名和有效期。如果验证成功,服务器认为用户是合法的,并处理请求。

三、代码示例:使用 JWT 实现登录认证

1. 项目环境

我们使用 Java 和 Spring Boot 框架来实现一个简单的登录系统,并使用 io.jsonwebtoken 库来处理 JWT。

2. 引入依赖

pom.xml 中添加以下依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <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>
</dependencies>

3. 代码实现

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.web.bind.annotation.*;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api")
public class AuthController {

    // 密钥,用于签名和验证 JWT
    private static final String SECRET_KEY = "your_secret_key";
    // 有效期,设置为 30 分钟
    private static final long EXPIRATION_TIME = 30 * 60 * 1000;

    // 模拟用户数据库
    private static final Map<String, String> users = new HashMap<>();

    static {
        users.put("user1", "password1");
        users.put("user2", "password2");
    }

    // 生成 JWT
    private String generateToken(String username) {
        Date now = new Date();
        Date expiration = new Date(now.getTime() + EXPIRATION_TIME);

        return Jwts.builder()
               .setSubject(username)
               .setIssuedAt(now)
               .setExpiration(expiration)
               .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
               .compact();
    }

    // 验证 JWT
    private Claims validateToken(String token) {
        try {
            return Jwts.parser()
                   .setSigningKey(SECRET_KEY)
                   .parseClaimsJws(token)
                   .getBody();
        } catch (Exception e) {
            return null;
        }
    }

    // 登录接口
    @PostMapping("/login")
    public String login(@RequestBody Map<String, String> credentials) {
        String username = credentials.get("username");
        String password = credentials.get("password");

        if (users.containsKey(username) && users.get(username).equals(password)) {
            return generateToken(username);
        }
        return "Invalid credentials";
    }

    // 受保护的接口
    @GetMapping("/protected")
    public String protectedResource(@RequestHeader("Authorization") String token) {
        if (token == null ||!token.startsWith("Bearer ")) {
            return "Token is missing";
        }
        token = token.substring(7);
        Claims claims = validateToken(token);
        if (claims != null) {
            return "Welcome, " + claims.getSubject();
        }
        return "Invalid token";
    }
}

4. 代码解释

  • generateToken 方法:根据用户的用户名生成一个 JWT,包含用户名、签发时间和有效期。
  • validateToken 方法:验证 JWT 的签名和有效期,如果验证成功,返回载荷信息。
  • /login 接口:处理用户的登录请求,验证用户名和密码,如果验证成功,生成并返回 JWT。
  • /protected 接口:受保护的接口,需要在请求头部中包含有效的 JWT 才能访问。

5. 测试

你可以使用 Postman 或者其他工具来测试接口:

  • 登录请求
    发送一个 POST 请求到 http://localhost:8080/api/login,请求体为 {"username": "user1", "password": "password1"}
  • 受保护的请求
    发送一个 GET 请求到 http://localhost:8080/api/protected,并在请求头中添加 Authorization: Bearer <your_token>

四、JWT 的安全注意事项

  • 密钥安全:密钥是 JWT 签名和验证的关键,必须妥善保管,避免泄露。
  • 有效期设置:合理设置 JWT 的有效期,避免过长的有效期导致安全风险。
  • 防止重放攻击:可以使用一次性令牌或添加时间戳等方式来防止重放攻击。

五、总结

JWT 是一种简单而强大的身份验证机制,适用于各种前后端分离的项目。通过本文的介绍,你应该对 JWT 的原理、工作流程和使用方法有了更深入的了解。在实际开发中,要注意 JWT 的安全问题,确保系统的安全性。希望本文能帮助你在项目中成功应用 JWT 进行登录认证。