概述
记录jwt加解密的demo。
JSON Web Token (JWT) 是一种开放标准 (RFC 7519),用于在网络应用环境间安全地传输信息。JWT 通常用于身份验证和信息交换,因为它可以被签名和加密,确保数据的完整性和隐私性。
JWT 的基本结构
JWT 由三部分组成,每部分之间用点号(.
)分隔:
- Header(头部)
- Payload(载荷)
- Signature(签名)
1. Header(头部)
头部通常包含两部分信息:令牌的类型(即 JWT)和所使用的签名算法(如 HMAC SHA256 或 RSA)。
示例:
{"alg": "HS256","typ": "JWT"
}
2. Payload(载荷)
载荷部分包含声明(claims),声明是用来传递信息的键值对。JWT 规范定义了几种标准的声明,但你也可以自定义声明。
常见的标准声明包括:
- iss (Issuer):签发者
- sub (Subject):主题
- aud (Audience):受众
- exp (Expiration Time):过期时间
- nbf (Not Before):生效时间
- iat (Issued At):签发时间
- jti (JWT ID):唯一标识符
示例:
{"sub": "1234567890","name": "John Doe","admin": true,"userInfo": {"email": "john.doe@example.com","roles": ["user", "admin"]},"iat": 1516239022
}
3. Signature(签名)
签名部分用于验证消息在传输过程中没有被篡改,并且可以验证 JWT 的接收方是否信任 JWT 的签发方。签名是通过对头部和载荷进行 Base64Url 编码,然后用指定的算法(如 HMAC SHA256)进行签名生成的。
签名生成步骤:
- 将头部和载荷分别进行 Base64Url 编码。
- 将编码后的头部和载荷用点号(
.
)连接起来。 - 使用指定的算法和密钥对连接后的字符串进行签名。
示例:
String encodedHeader = Base64Url.encode(headerJson);
String encodedPayload = Base64Url.encode(payloadJson);
String unsignedToken = encodedHeader + "." + encodedPayload;
String signature = sign(unsignedToken, secretKey);
String jwt = unsignedToken + "." + signature;
JWT 的优点
- 无状态:JWT 是无状态的,服务器不需要保存会话信息,可以轻松地扩展到多服务器架构。
- 安全性:JWT 可以被签名,确保数据的完整性;还可以被加密,确保数据的隐私性。
- 自包含:JWT 包含了所有的必要信息,可以在一个紧凑的字符串中传递。
- 跨域支持:JWT 可以在不同的域名之间传递,适用于单点登录(SSO)等场景。
JWT 的使用场景
- 身份验证:用户登录后,服务器生成一个 JWT 并返回给客户端。客户端在后续请求中携带 JWT,服务器验证 JWT 的有效性。
- 信息交换:在不同系统之间传递信息,确保信息的完整性和安全性。
- 授权:根据 JWT 中的声明信息,决定用户是否有权限访问特定资源。
引入依赖
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>
代码示例1
创建 JWT
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Date;public class JwtUtil {private static final String SECRET_KEY = "your-secret-key-must-be-at-least-512-bits-long"; // 确保密钥足够长public static String createToken(Map<String, Object> claims, long expiration) {Key signingKey = new SecretKeySpec(SECRET_KEY.getBytes("UTF-8"), SignatureAlgorithm.HS512.getJcaName());return Jwts.builder().setClaims(claims).setExpiration(new Date(System.currentTimeMillis() + expiration)).signWith(SignatureAlgorithm.HS512, signingKey).compact();}
}
解析 JWT
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Map;public class JwtUtil {private static final String SECRET_KEY = "your-secret-key-must-be-at-least-512-bits-long"; // 确保密钥足够长public static Map<String, Object> parseToken(String token) {Key signingKey = new SecretKeySpec(SECRET_KEY.getBytes("UTF-8"), SignatureAlgorithm.HS512.getJcaName());Claims claims = Jwts.parser().setSigningKey(signingKey).parseClaimsJws(token).getBody();return (Map<String, Object>) claims.get("userInfo");}
}
代码示例2
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;public class UserInfo解析 {private static final String TOKEN_PREFIX = "Basic ";private static final String BASIC_TOKEN_KEY = "123456789poiuytrewqzxcvbnmlkjhgfdsa";/*** Token缺省过期时间是30分钟*/private static final Long TOKEN_EXPIRATION = 1800000L;private static final String CLAIM_KEY_CREATEDTIME = "CreatedTime";private static final String USER_INFO_STRING = "userInfo";public static void main(String[] args) {Map<String, Object> map = new HashMap<>();Map<String, Object> userInfoMap = new HashMap<>();map.put("userInfo", userInfoMap);userInfoMap.put("name", "张三");userInfoMap.put("age", "26");userInfoMap.put("address", "河北石家庄");String token = encryptToken(map);Map<String, Object> userInfo = decryptToken(token);log.info(userInfo.toString());}public static Map<String, Object> decryptToken(String token){String jetToken = token.substring(TOKEN_PREFIX.length());Key signingKey = new SecretKeySpec(BASIC_TOKEN_KEY.getBytes(), SignatureAlgorithm.HS512.getJcaName());Claims claims = Jwts.parser().setSigningKey(signingKey).parseClaimsJws(jetToken).getBody();return claims.get(USER_INFO_STRING, Map.class);}public static String encryptToken(Map<String, Object> map){long createTime = System.currentTimeMillis();map.put(CLAIM_KEY_CREATEDTIME, createTime);String token = Jwts.builder().setClaims(map).setExpiration(new Date(createTime + TOKEN_EXPIRATION)).signWith(SignatureAlgorithm.HS512, BASIC_TOKEN_KEY.getBytes()).compact();return TOKEN_PREFIX + token;}}
执行结果
注意事项
- 密钥管理:确保密钥的安全性,不要泄露。
- 过期时间:合理设置过期时间,防止 JWT 被长期滥用。
- 签名算法:选择合适的签名算法,确保安全性。
- 敏感信息:不要在 JWT 中存储敏感信息,如密码。