1、JWT介绍
定义:JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案
JWT官网:https://jwt.io/
由于HTTP协议是无状态的,如果想判定一个接口是否被认证后访问,就需要借助cookie或者session会话机制进行判定,但是由于现在的系统架构不止一台服务器,此时要借助数据库或者全局缓存做存储,这种方案受限太多。
JWT提供提供了一种更好的方案,由令牌发布者发布令牌,其他服务持有令牌,当接口访问时需要提供令牌,并由令牌发布者去验证这个令牌。优点是简单快捷、不需要依赖任何第三方就能实现身份认证,缺点是任何持有令牌的用户都可以认证通过。
2、JWT的数据结构
正常的JWT数据结构如下:
JWT是一段字符串,中间由.分割为三部分。
分别是
Header:头部
Payload:负载
Payload:负载
JWT的格式即为:Header.Payload.Signature
2.1、Header头部
Header部分是一个JSON对象,描述JWT的元数据。
格式如下:
{
“alg”: “HS256”,
“typ”: “JWT”
}
alg属性表示签名的算法(algorithm),默认值为HMAC SHA256(HS256);
typ属性表示这个令牌(token)的类型,JWT的令牌值为"JWT"。
2.2、Payload负载
Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段。
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
除了官方字段还可以使用withClaim(“name”, name)定义私有字段。
例如
{
“name”:“zhansan”
}
注意:JWT默认是明文展示,任何人都可以读取到,所以此处不要放私密信息。
2.3、Signature签名
Signature 部分是对前两部分的签名,防止数据篡改。
指定一个密钥(secret),使用Header里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256(base64UrlEncode(header) + “.” +base64UrlEncode(payload),secret);
得到签名后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用(.)分隔,并返回给用户。
2.4、Base64URL字符串编码
上文提到Header和Payload字符编码采用的是Base64URL,和Base64算法类似。Base64算法中还有"+","/","=“字符串,在URL里面有特殊含义,所以要被替换掉:”=“省略、”+“替换成”-","/“替换成”_",这就是Base64URL算法。
3、JWT实现应用
Maven依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.8.0</version>
</dependency>
实现代码
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class TokenUtil {
private static Logger logger = LoggerFactory.getLogger(TokenUtil.class);
/**
* Token的过期时间
*/
private static final Integer EXPIRE_TIME = 24 * 60 * 60;
/**
* Token的私钥
*/
private static final String TOKEN_SECRET = "token_secret";
public static String createToken(String userName) {
try {
// 设置过期时间
Calendar nowTime = Calendar.getInstance();
nowTime.add(Calendar.SECOND, EXPIRE_TIME);
Date expireDate = nowTime.getTime();
//私钥和加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
// 设置头部信息
Map<String, Object> header = new HashMap<>();
header.put("typ", "JWT");
header.put("alg", "HS256");
// 返回token字符串
return JWT.create()
.withHeader(header)
.withClaim("userName", userName)
.withIssuedAt(new Date())
.withExpiresAt(expireDate)
.sign(algorithm);
} catch (Exception e) {
logger.error("CreateToken Error", e);
return null;
}
}
/**
* 检验token是否正确
*/
public static boolean verify(String token) {
try {
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier verifier = JWT.require(algorithm).build();
//未验证通过会抛出异常
verifier.verify(token);
return true;
} catch (Exception e) {
logger.error("verify Error", e);
return false;
}
}
/**
* 获取自定字段
*/
public static Map<String, Claim> getClaims(String token) {
try {
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = verifier.verify(token);
return decodedJWT.getClaims();
} catch (Exception e) {
logger.error("getClaims Error", e);
throw new RuntimeException(e);
}
}
/**
* 获取JWT
*/
public static DecodedJWT getDecoded(String token) {
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).build();
DecodedJWT jwt = verifier.verify(token);
return jwt;
} catch (UnsupportedEncodingException e) {
logger.error("getDecoded Error", e);
return null;
}
}
public static void main(String[] args) {
String token = TokenUtil.createToken("zhansan");
System.out.println(TokenUtil.verify(token));
Map<String, Claim> claims = TokenUtil.getClaims(token);
Claim userName = claims.get("userName");
System.out.println(userName.asString());
DecodedJWT jwt = TokenUtil.getDecoded(token);
System.out.println(jwt.getType());
System.out.println(jwt.getPayload());
System.out.println(jwt.getSignature());
System.out.println(jwt.getToken());
}
}