我们一起聊一聊JWT的那些事
一切美好,如期而至…
什么是JWT
JWT,全称为 JSON Web Token,是一种用于在网络上安全地传递信息的开放标准(RFC 7519)。JWT 是一种紧凑且独立的方式,用于在各方之间以 JSON 对象形式安全地传输信息。该信息可以被验证和信任,因为它是经过数字签名的。JWT 可以使用秘密(使用 HMAC 算法)或使用公钥/私钥对(使用 RSA 或 ECDSA 算法)进行签名。
JWT 通常用于身份验证和信息交换。在身份验证方面,当用户成功登录后,服务器会生成一个包含用户标识信息的 JWT,并将其发送回客户端。客户端随后可以在每个后续请求中包含 JWT,以证明其身份。在信息交换方面,由于 JWT 的信息是经过签名的,接收方可以验证其来源和完整性。
JWT 的结构如下:
xxxxx.yyyyy.zzzzz
其中,每一部分由点号分隔:
-
第一部分是头部(Header),通常包含两部分信息:令牌类型(JWT)和所使用的签名算法,例如 HMAC SHA256 或 RSA。
-
第二部分是载荷(Payload),包含声明(claims)。声明是关于实体(通常是用户)和其他数据的声明。有三种类型的声明:注册的声明,公共的声明和私有的声明。
-
第三部分是签名(Signature),用于验证消息在传递过程中没有被篡改,并确保消息的发送方是可信的。
JWT 的优点包括:
-
自包含: JWT 包含了所有需要的信息,减少了在服务器存储会话状态的需要。
-
跨域: 由于 JWT 是在各方之间传递的标准格式,可以在不同域之间轻松传递信息。
-
安全性: 当使用数字签名时,可以确保 JWT 的来源和完整性。
然而,需要注意的是,为了确保安全性,不应该在 JWT 中存储敏感信息,尤其是在使用对称加密算法的情况下。对于敏感信息,最好在服务器端进行验证。
和普通的token有啥区别子
JSON Web Token(JWT)和普通的 token 之间有一些关键区别,主要涉及到它们的结构、用途和安全性等方面:
-
结构差异:
- 普通 Token: 通常是一个简单的字符串,可能是随机生成的一串字符,例如 UUID(Universally Unique Identifier)。
- JWT: 具有明确定义的结构,包含头部、载荷和签名三个部分,并以点号分隔。这种结构使得 JWT 可以携带更多的信息,并且具有更灵活的用途。
-
内容携带:
- 普通 Token: 只是一个标识符,不携带其他有关用户或实体的信息。
- JWT: 可以携带一些声明(claims),这些声明包含有关用户、角色、权限等信息。由于 JWT 是基于 JSON 格式的,因此它可以以一种结构化的方式携带更多的信息。
-
签名和验证:
- 普通 Token: 通常需要在服务器端进行验证,但不一定使用数字签名或加密。
- JWT: 通常使用数字签名来验证其真实性。这使得 JWT 在传递过程中能够被验证,确保它没有被篡改。JWT 还支持使用非对称加密来提供额外的安全性。
-
安全性:
- 普通 Token: 可能存在被伪造或截获的风险,因为它没有内置的安全机制。
- JWT: 通过数字签名或加密,提供了一定程度的安全性。如果使用对称加密,需要确保密钥的安全性;如果使用非对称加密,可以更安全地传递公钥。
-
用途:
- 普通 Token: 通常用于简单的身份验证,例如在会话中保存用户的登录状态。
- JWT: 由于其结构和灵活性,可用于更广泛的用途,包括身份验证、信息交换和声明传递。
总体而言,JWT 是一种更为灵活、结构化且安全的令牌,适用于需要携带更多信息和在跨域环境中进行安全传递的场景。普通 Token 更适合简单的身份验证场景。选择使用哪种令牌取决于具体的应用需求和安全要求。
如何生成JWT
前端生成
生成 JSON Web Tokens(JWT)涉及以下步骤:
-
选择一个密钥: JWT 使用密钥进行签名以确保数据的完整性和来源。密钥可以是对称密钥(HMAC算法)或非对称密钥(RSA或ECDSA算法)。
-
构建JWT的头部(Header): JWT的头部包含有关令牌的元信息,如算法和令牌类型。这部分信息通常以Base64编码的JSON字符串表示,并放置在JWT的第一部分。
-
构建JWT的载荷(Payload): 载荷包含有关JWT主体(subject)的声明和其他信息。与头部一样,这部分信息也是以Base64编码的JSON字符串表示,并放置在JWT的第二部分。
-
对头部和载荷进行签名: 使用选择的算法和密钥对头部和载荷进行签名。签名是通过将Base64编码的头部和载荷字符串与密钥一起进行加密而生成的。
-
将头部、载荷和签名组合成JWT: 将Base64编码的头部、Base64编码的载荷和签名以点号分隔组合在一起形成JWT。
下面是一个简单的例子,使用Node.js中的jsonwebtoken
库生成JWT:
const jwt = require('jsonwebtoken');
// 构建头部
const header = {
"alg": "HS256", // HMAC SHA-256算法
"typ": "JWT"
};
// 构建载荷
const payload = {
"sub": "1234567890", // 主体标识
"name": "John Doe",
"iat": Math.floor(Date.now() / 1000) // 签发时间(当前时间)
};
// 选择密钥
const secretKey = "yourSecretKey";
// 生成JWT
const token = jwt.sign(payload, secretKey, { header });
console.log(token);
在实际应用中,建议使用更安全的随机生成的密钥,并根据具体的需求选择适当的算法和配置选项。生成JWT的代码示例可能会根据所选的编程语言和库而有所不同,但上述步骤是通用的。
后端生成
在Java中,你可以使用 io.jsonwebtoken
库来生成 JSON Web Tokens(JWT)。以下是一个简单的例子:
首先,你需要将 jjwt
库添加到你的项目中。如果使用 Maven,可以在 pom.xml
文件中添加以下依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
然后,你可以使用以下Java代码生成JWT:
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class JwtGenerator {
public static void main(String[] args) {
// 构建头部
String algorithm = SignatureAlgorithm.HS256.getJcaName();
String header = "{\"alg\":\"" + algorithm + "\",\"typ\":\"JWT\"}";
// 构建载荷
String subject = "1234567890"; // 主体标识
String name = "John Doe";
long nowMillis = System.currentTimeMillis();
Date iat = new Date(nowMillis); // 签发时间(当前时间)
// 选择密钥
String secretKey = "yourSecretKey";
// 生成JWT
String token = Jwts.builder()
.setHeader(header)
.setSubject(subject)
.claim("name", name)
.setIssuedAt(iat)
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
System.out.println(token);
}
}
请确保替换示例中的密钥和其他参数为你实际的值。这个例子中使用了 HMAC SHA-256 算法,但你可以根据需求选择其他算法。在实际应用中,密钥应该是安全的,并且可能需要使用环境变量或其他方式来管理。
还有那些库
除了 io.jsonwebtoken
,还有一些其他常用的Java库可以用于生成和处理JSON Web Tokens(JWT)。以下是一些常见的JWT库:
-
Nimbus JOSE + JWT:
- 官方网站: Nimbus JOSE + JWT
- 主要功能:
- 提供了处理JOSE(JSON Object Signing and Encryption)和JWT的Java库。
- 支持各种JWT相关的标准和规范,如JWT、JWS(JSON Web Signature)、JWE(JSON Web Encryption)等。
- 提供了易于使用的API,支持生成和验证JWT。
-
Java-JWT:
- GitHub地址: Java-JWT
- 主要功能:
- Auth0开发的Java库,用于生成和验证JWT。
- 支持标准的JWT算法,如HMAC SHA256、RS256等。
- 提供了简洁的API,适用于各种JWT用例。
-
JJWT:
- GitHub地址: JJWT
- 主要功能:
- 一个简单的Java库,用于生成、解析和验证JWT。
- 支持各种算法,包括HMAC SHA256、RS256等。
- 提供了流畅的API,易于使用。
当然,还有HuTool工具类,就不多赘述了
选择使用哪个库取决于你的具体需求、项目的特点以及个人偏好。在使用之前,请仔细查阅相关文档,了解库的功能、性能和安全性。
JWT的解析和验证
解析和验证 JSON Web Tokens(JWT)通常包括以下步骤:
-
拆分 JWT: JWT 由三部分组成,分别是头部(Header)、载荷(Payload)、签名(Signature),它们之间用点号分隔。首先,将 JWT 字符串拆分成这三个部分。
-
解码 Base64: 对头部和载荷进行 Base64 解码,得到 JSON 字符串。
-
解析 JSON: 将解码后的头部和载荷 JSON 字符串解析为 JSON 对象。
-
验证签名(可选): 如果 JWT 使用签名算法进行签名,需要对头部、载荷和签名进行验证。验证的步骤通常包括获取密钥(对称或非对称密钥),使用相同的签名算法对头部和载荷进行签名,然后比较生成的签名与 JWT 中的签名是否匹配。
以下是一个使用 Java 的示例,使用 io.jsonwebtoken
库进行 JWT 解析和验证的过程:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
public class JwtParser {
public static void main(String[] args) {
// 要解析和验证的 JWT 字符串
String jwtString = "yourJwtStringHere";
try {
// 解析 JWT
Jws<Claims> jws = Jwts.parserBuilder()
.setSigningKey("yourSecretKey") // 设置密钥
.build()
.parseClaimsJws(jwtString);
// 获取载荷(Claims)
Claims claims = jws.getBody();
// 在此可以获取 JWT 中的信息,例如:
String subject = claims.getSubject();
String name = (String) claims.get("name");
// 其他声明的获取方法...
// 验证通过,可以继续处理业务逻辑
System.out.println("JWT验证通过");
} catch (ExpiredJwtException e) {
// JWT 过期异常
System.out.println("JWT已过期");
} catch (SignatureException e) {
// JWT 签名异常
System.out.println("JWT签名验证失败");
} catch (Exception e) {
// 其他异常
System.out.println("JWT解析失败");
}
}
}
请注意,上述代码中的密钥和其他参数需要根据你的实际情况进行调整。此外,对于非对称加密的情况,需要提供公钥进行验证。在实际应用中,也可以考虑使用库提供的更复杂的配置选项和错误处理机制。