1、什么是jwt
简介
- 全称:JSON Web Token
- 定义了一种简介的、自包含的格式,用于在通信双方以json数据格式安全的传输信息,由于数字签名的存在,这些消息都是可靠的
- JWTs可以使用密钥(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
2、JWT的使用
JWTs通常用于以下场景:
身份验证:在用户登录后,服务器会生成一个包含用户信息的JWT,并将其发送给客户端。客户端随后可以使用这个JWT来证明自己的身份,访问受保护的资源。
信息交换:JWT可以安全地在两个系统之间传输信息,因为这些信息是经过签名的,接收方可以验证发送方的身份以及信息的完整性。
JWT由三部分组成,分别是Header(头部)、Payload(负载)和Signature(签名):
Header:通常包含两部分信息,token的类型(即JWT)和采用的加密算法(如HS256)。
示例:{ "alg": "HS256", "typ": "JWT" }
然后,这个头部会被Base64Url编码。
Payload:包含一些用户信息或其他需要传递的数据。这些信息会被Base64Url编码,但是即使编码后也易于解码,因此不应该包含敏感信息。
示例:{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
其中,
sub
是主题(即用户的标识),name
是用户名,iat
是签发时间。Signature:是用于验证消息在传递过程中未被篡改的签名。它通过组合Header和Payload,然后使用密钥和相应的签名算法生成。
示例:HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
JWT的一个重要特性是它的自包含性。由于所有必要的信息都包含在JWT自身,因此它不依赖于外部的验证。这使得JWT非常适合在不同域之间传递信息。
然而,JWT也有一些安全考虑。例如,如果JWT被泄露,由于它是自验证的,攻击者可以无限期使用它,直到它过期。因此,在使用JWT时,需要确保它的传输是安全的(例如,通过HTTPS),并且对于敏感操作,应该使用短期有效的JWT。
3、JWT简单入门
在Java项目中使用JWT(JSON Web Token),你需要添加相应的依赖到你的项目构建配置文件中。根据你使用的构建工具不同,添加依赖的方式也会有所不同。以下是两种常见的构建工具Maven和Gradle的依赖添加方法:
Maven
如果你的项目使用Maven构建系统,你可以在项目的pom.xml
文件中添加以下依赖:
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.14.0</version> <!-- 请使用最新的版本号 -->
</dependency>
Gradle
如果你的项目使用Gradle构建系统,你可以在build.gradle
文件中添加以下依赖:
compile 'com.auth0:java-jwt:3.14.0' // 请使用最新的版本号
在添加了依赖之后,你可以通过执行Maven或Gradle的构建命令来下载并添加JWT库到你的项目中,这样你就可以开始使用JWT相关的功能了。
创建
getJwt
方法
这个方法用于生成JWT。步骤如下:
- 创建一个
Map
来存储JWT的载荷(claims),并设置用户的ID和用户名。 - 使用
Jwts.builder()
创建一个新的JWT构建器。 - 通过
.setClaims(claims)
方法将载荷添加到JWT中。 - 使用
.signWith(SignatureAlgorithm.HS256, "xiaolin")
方法设置签名算法和签名密钥(在这里是 "xiaolin")。 - 通过
.setExpiration(new Date(System.currentTimeMillis() + 2 * 3600 * 1000))
方法设置JWT的过期时间(这里设置为2小时后)。 - 使用
.compact()
方法生成JWT字符串。 - 打印生成的JWT字符串。
@Test
public void getJwt(){
//设置载荷
Map<String,Object> claims = new HashMap<>();
claims.put("id",1);
claims.put("username","tom");
//生成令牌
String token = Jwts.builder()
.setClaims(claims) //设置载荷
.signWith(SignatureAlgorithm.HS256, "xiaolin") //设置加密算法和签名
.setExpiration(new Date(System.currentTimeMillis() + 2 * 3600 * 1000)) //设置过期时间
.compact();
System.out.println("令牌" + token);
}
创建
parserJwt
方法
这个方法用于解析JWT。步骤如下:
- 使用
Jwts.parser()
创建一个新的JWT解析器。 - 通过
.setSigningKey("xiaolin")
方法设置签名密钥,以便解析器可以验证签名。 - 使用
.parseClaimsJws(token)
方法解析JWT,并获取其载荷。 - 从载荷中提取ID和用户名。
- 打印提取的ID和用户名。
@Test
public void parserJwt(){
//解析token
Claims claims = Jwts.parser()
.setSigningKey("xiaolin") //设置签名
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNzEwOTM5MDA3LCJ1c2VybmFtZSI6InRvbSJ9.Ec5VTcB8Gj92ZzrGuZE0mAX3onkn-n9fqJjwwi2Zqss")
.getBody();
//获取载荷数据
Integer id = (Integer) claims.get("id");
String username = (String) claims.get("username");
//打印
System.out.println(id+":"+username);
}
4、代码案例
为了提供一个实际的代码实现示例,我们将创建一个简单的Java Web应用程序,使用JWT进行用户身份验证和授权。这个例子将包括用户登录、生成JWT、验证JWT和使用JWT访问受保护资源的基本流程。
1. 添加JWT依赖
首先,确保你的pom.xml
文件中包含了JWT的依赖:
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.14.0</version> <!-- 请使用最新的版本号 -->
</dependency>
2. 创建JWT工具类
创建一个JWT工具类,用于生成和验证JWT:
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtUtil {
private static final String SECRET_KEY = "your-secret-key"; // 密钥应该安全存储
public static String generateToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1小时后过期
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public static Map<String, Object> getClaims(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
}
3. 用户登录
创建一个用户登录的端点,如果用户认证成功,返回JWT:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class AuthController {
public String login(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 这里应该有用户认证逻辑,例如检查用户名和密码
// 假设认证成功
Map<String, Object> claims = new HashMap<>();
claims.put("username", "user");
claims.put("role", "admin");
String token = JwtUtil.generateToken(claims);
// 设置HTTP响应头,以便现代浏览器不会缓存响应
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
return token;
}
}
4. 受保护的资源
创建一个受保护资源的端点,只有带有有效JWT的请求才能访问:
public class ProtectedResourceController {
public String getResource(HttpServletRequest request) {
String token = request.getHeader("Authorization").substring("Bearer ".length()); // 从HTTP请求头获取JWT
Map<String, Object> claims = JwtUtil.getClaims(token);
// 根据claims中的信息进行权限验证
// 假设验证成功
if ("admin".equals((String) claims.get("role"))) {
return "Welcome admin!";
} else {
return "Access denied!";
}
}
}
5. 启动服务器
最后,你需要一个服务器来运行你的应用程序。如果你使用Spring Boot,你可以创建一个带有@SpringBootApplication
注解的主类,并使用SpringBootApplication.run()
方法来启动服务器。
请注意,这个例子是一个非常简化的版本,仅用于演示JWT的基本用法。在实际的生产环境中,你需要考虑更多的安全措施,例如使用HTTPS、防止CSRF攻击、密钥的安全管理等。此外,用户认证逻辑应该与数据库或其他用户存储系统集成,以验证用户的凭据。