赞
踩
什么是JWT(JSON Web Token)?
JWT是一种用于身份验证和授权的开放标准(RFC 7519),它是基于JSON格式的轻量级安全令牌。JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。通常,JWT被用于在不同的系统之间传递安全性的声明信息,以便用户在跨域应用中进行身份验证。
JWT有什么好处,能干啥?
JWT的主要优点包括:
JWT能够实现的功能包括:
typ
)和使用的签名算法(alg
)。示例:{"alg": "HS256", "typ": "JWT"}
{"sub": "1234567890", "name": "John Doe", "admin": true}
系统中的JWT是如何使用的,在什么时候使用的?
在一个系统中,JWT通常用于实现用户身份验证和授权。当用户登录成功后,服务器会生成一个JWT令牌并发送给客户端,客户端保存该令牌,并在后续的请求中将令牌添加到请求头或请求参数中。服务器在收到请求时,会解析JWT令牌并验证其合法性和有效性,从而判断用户是否已经登录以及是否有权限访问请求的资源。
JWT的使用可以简化系统的身份验证和授权流程,提高系统的安全性和性能。同时,由于JWT本身是无状态的,不需要在服务器端保存会话信息,使得系统更易于扩展和维护。引入jwt,并使用生成token
当使用JWT进行用户身份验证和授权时,通常包含以下步骤:
用户登录认证:
UserService
类中的authenticateUser
方法验证用户名和密码是否正确。生成JWT令牌:
JwtUtils
工具类中的generateJwtToken
方法生成JWT令牌。generateJwtToken
方法接收三个参数:
subject
:即载荷(Payload)中的"sub"字段,通常用于标识用户的唯一标识,例如用户ID、用户名等。expirationMillis
:令牌的有效期,以毫秒为单位。令牌过期后将无效,需要重新登录获取新的令牌。SECRET_KEY
:用于签名的密钥,确保密钥保密,不要泄露给他人。generateJwtToken
方法会使用Jwts
类的builder
方法构建JWT令牌,并设置令牌的头部(Header)、载荷(Payload)、过期时间(Expiration)等信息。返回JWT令牌:
客户端携带JWT令牌:
解析和验证JWT令牌:
JwtUtils
工具类中的parseJwtToken
方法对JWT令牌进行解析和验证。parseJwtToken
方法接收一个JWT令牌作为参数,并使用之前设置的密钥SECRET_KEY
对令牌进行解析。parseJwtToken
方法将返回载荷(Payload)中的"sub"字段的值,即之前设置的用户唯一标识。用户访问控制:
客户端生成令牌: public void generateJWT() { JwtBuilder builder = Jwts.builder(); String token = builder // 头部 .setHeaderParam("typ", "JWT") .setHeaderParam("alg", "HS256") // 载荷数据/过期时间 .claim("id", 10001) .claim("nickName", "老王") // 设置过期时间,这里设置为当前时间加上一小时 .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 设置JWT的唯一标识(ID),这里使用UUID生成唯一ID .setId(UUID.randomUUID().toString()) // 签名,使用HS256算法和指定的密钥(key)进行签名 .signWith(SignatureAlgorithm.HS256, key) // 构造JWT令牌 .compact(); System.out.println(token); } /** * 服务器解析JWT */ @Test public void verifyJWT() { // 要解析的JWT令牌 String jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTAwMDEsIm5pY2tOYW1lIjoi6ICB546LIiwiZXhwIjoxNjkwMjc0NTg3LCJqdGkiOiIxZTY0MWExMS0yMjJkLTQ4YjMtODQ1OS05ZTlmNDUxZjIyZGQifQ.nby2Lp7nx4BLuk3SFAk0ZLPbhliLQ0l3eWOADnX5Kfc"; // 创建JwtParser对象,用于解析JWT令牌 JwtParser parser = Jwts.parser(); // 解析JWT令牌并验证签名 Jws<Claims> claimsJws = null; try { claimsJws = parser .setSigningKey(key) // 设置验证签名所使用的密钥(与生成JWT时使用的密钥相同) .parseClaimsJws(jwt); // 解析JWT令牌 Claims body = claimsJws.getBody(); // 获取解析后的JWT令牌的载荷(Payload) System.out.println(body); // 输出解析后的JWT令牌的载荷内容 } catch (Exception e) { // 验证失败,抛出异常 throw new RuntimeException(e); } }
借助jjwt库来生成和解析JWT令牌。
首先,确保在项目中导入jjwt库的依赖:
<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>
接下来,我们定义一个简单的JWT工具类来生成和解析JWT令牌:
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; public class JwtUtils { private static final String SECRET_KEY = "your-secret-key"; // 替换为自己的密钥 public static String generateJwtToken(String subject, long expirationMillis) { Date now = new Date(); Date expirationDate = new Date(now.getTime() + expirationMillis); return Jwts.builder() .setSubject(subject) .setIssuedAt(now) .setExpiration(expirationDate) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public static String parseJwtToken(String jwtToken) { Claims claims = Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(jwtToken) .getBody(); return claims.getSubject(); } }
在上面的代码中,我们定义了generateJwtToken
方法用于生成JWT令牌,parseJwtToken
方法用于解析JWT令牌。需要注意的是,我们在生成和解析JWT令牌时都使用了同一个密钥SECRET_KEY
,确保密钥保密,不要泄露给他人。
这里的密钥可以使用算法生成
public static String generateRandomKey() {
// 生成256位的随机字节数组
byte[] keyBytes = new byte[32];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(keyBytes);
// 将字节数组转换为Base64编码的字符串
String key = Base64.getEncoder().encodeToString(keyBytes);
System.out.println(key + "==============");
return key;
}
在这段代码中,我们使用Jwts.builder()
来创建JWT令牌(JSON Web Token)。Jwts
是jjwt库中的一个类,用于构建JWT令牌。
接下来,我们可以使用这个JWT工具类来实现用户登录和认证的功能:
public class UserService {
// 模拟用户数据库
private Map<String, String> userDatabase = new HashMap<>();
{
userDatabase.put("user1", "password1");
userDatabase.put("user2", "password2");
}
public boolean authenticateUser(String username, String password) {
String storedPassword = userDatabase.get(username);
return storedPassword != null && storedPassword.equals(password);
}
}
在上面的示例中,我们假设有一个UserService
类,其中authenticateUser
方法用于验证用户的用户名和密码是否正确。请注意,这里只是一个简化的模拟用户数据库,实际应用中应该使用更安全和可靠的方式来存储和验证用户信息。
接下来,我们可以在主类中使用这些组件来演示JWT的使用:
public class Main { public static void main(String[] args) { UserService userService = new UserService(); // 用户登录,验证用户名和密码是否正确 String username = "user1"; String password = "password1"; boolean isAuthenticated = userService.authenticateUser(username, password); if (isAuthenticated) { // 用户登录成功,生成JWT令牌并返回给客户端 String jwtToken = JwtUtils.generateJwtToken(username, 3600000); // 令牌有效期1小时 System.out.println("JWT Token: " + jwtToken); // 客户端在后续请求中携带JWT令牌,服务器端解析令牌并验证用户身份 String parsedUsername = JwtUtils.parseJwtToken(jwtToken); System.out.println("Parsed Username: " + parsedUsername); } else { System.out.println("Authentication failed. Invalid username or password."); } } }
在这个示例中,我们首先进行用户登录认证,如果用户名和密码验证通过,我们就生成JWT令牌并返回给客户端。在后续的请求中,客户端需要在请求头或请求参数中携带这个JWT令牌,服务器端解析令牌并验证用户身份,从而实现了用户的身份验证和授权功能。
请注意,实际应用中,我们应该更加完善和安全地管理JWT令牌,例如设置更短的过期时间、使用HTTPS等。此外,建议使用第三方库来处理JWT令牌的生成和解析,以确保安全性和正确性。
关于代码的解释
下面逐个方法来解释这段代码的含义:
setSubject(subject)
:设置JWT令牌的主题(Subject)。在JWT中,主题是令牌所针对的用户,通常用于标识用户的唯一标识,例如用户ID或用户名。这个方法用于将用户的唯一标识作为主题设置到JWT令牌中。setIssuedAt(now)
:设置JWT令牌的签发时间(Issued At)。在JWT中,签发时间表示令牌的创建时间。这个方法用于将当前时间设置为令牌的签发时间。setExpiration(expirationDate)
:设置JWT令牌的过期时间(Expiration)。在JWT中,过期时间表示令牌的有效期截止时间。在过期时间之后,令牌将失效,需要重新登录获取新的令牌。这个方法用于将指定的过期时间设置到JWT令牌中。signWith(SignatureAlgorithm.HS256, SECRET_KEY)
:对JWT令牌进行签名。在JWT中,签名是为了保证令牌的完整性和安全性。签名需要一个密钥来进行加密,确保只有服务器持有密钥才能验证令牌的有效性。这个方法使用指定的签名算法(此处使用HS256)和密钥(SECRET_KEY
)对令牌进行签名。compact()
:将JWT令牌压缩成一个字符串形式。在上面的代码中,Jwts.builder()
用于构建JWT令牌的各个部分,compact()
方法将JWT令牌压缩成最终的字符串形式,以便返回给客户端。综合起来,这段代码的作用是创建一个包含指定主题、签发时间和过期时间的JWT令牌,并使用指定的签名算法和密钥对令牌进行签名,最后将JWT令牌压缩成字符串形式并返回给客户端。生成的JWT令牌将包含在响应中,客户端可以在后续的请求中携带该令牌进行身份验证和授权。服务器在接收到带有JWT令牌的请求后,可以使用相同的密钥对令牌进行解析和验证,以确认令牌的合法性和有效性。
在这段代码中,我们定义了一个静态方法parseJwtToken
,用于解析JWT令牌并获取其中的主题(Subject)信息。parseJwtToken
方法接收一个JWT令牌(字符串形式)作为参数,并返回解析后的主题信息。
下面逐个方法来解释这段代码的含义:
Jwts.parser()
:创建一个JWT解析器。在jjwt库中,Jwts
类的parser()
方法用于创建一个JWT解析器,用于解析JWT令牌的内容。setSigningKey(SECRET_KEY)
:设置JWT解析器的验证密钥。在解析JWT令牌时,需要使用相同的密钥来验证令牌的签名。这个方法使用之前生成JWT令牌时使用的密钥SECRET_KEY
来设置JWT解析器的验证密钥。parseClaimsJws(jwtToken)
:解析JWT令牌并获取Claims对象。在jjwt库中,parseClaimsJws
方法用于解析JWT令牌,并返回一个Jws<Claims>
对象,其中包含了JWT令牌的头部、载荷和签名等信息。getBody()
:从Jws<Claims>
对象中获取JWT令牌的载荷(Payload)。在JWT中,载荷部分是一个JSON对象,包含了令牌的各种信息,例如主题(Subject)、签发时间(Issued At)、过期时间(Expiration)等。这个方法获取JWT令牌的载荷,并返回一个Claims
对象,其中包含了令牌的所有信息。claims.getSubject()
:从Claims
对象中获取主题信息。在JWT令牌的载荷中,通常使用"sub"字段来标识主题,即令牌所针对的用户唯一标识,例如用户ID或用户名。这个方法获取JWT令牌的主题信息,并返回给调用方。综合起来,这段代码的作用是解析传入的JWT令牌,并从令牌的载荷中获取主题信息。这个主题信息通常用于标识令牌所针对的用户,服务器可以根据主题信息来验证用户身份和进行访问控制。如果JWT令牌的签名验证通过且令牌没有过期,parseJwtToken
方法将返回令牌的主题信息,否则可能会抛出异常或返回null,取决于解析过程中是否出现异常。
总结:
JWT的使用过程包括用户登录认证、生成JWT令牌、返回令牌给客户端、客户端携带令牌访问资源、服务器解析和验证令牌、根据令牌中的信息进行用户访问控制。JWT的无状态特性使得服务器无需保存会话信息,提高了系统的性能和可伸缩性。同时,使用合适的签名算法和保密的密钥,JWT令牌能够提供一定程度的安全性,确保令牌的完整性和安全性。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。