一、What is JWT
jwt 全称SON Web Token,是为了在网络应用环境间传递声明而执行的一种基于json的开放标准,jwt的声明一般被用来在身份提供者和服务提供者之间传递被认证的用户身份信息。
授权服务器将用户信息和授权范围序列化后放入一个JSON字符串,然后使用Base64进行编码,最终在授权服务器用私钥对这个字符串进行签名,得到一个JSON Web Token。假设其他所有的资源服务器都将持有一个RSA公钥,当资源服务器接收到这个在Http Header中存有Token的请求,资源服务器就可以拿到这个Token,并验证它是否使用正确的私钥签名(是否经过授权服务器签名,也就是验签)。验签通过,反序列化后就拿到Toekn中包含的有效验证信息。
二、JWT构成
1、Header
Header是由以下这个格式的Json通过Base64编码(编码不是加密,是可以通过反编码的方式获取到这个原来的Json,所以JWT中存放的一般是不敏感的信息)生成的字符串,Header中存放的内容是说明编码对象是一个JWT以及使用“SHA-256”的算法进行加密(加密用于生成Signature)
2、Payload
Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。
这个 JSON 对象也要使用 Base64URL 算法转成字符串。
3、Signature
Signature是由Header和Payload组合而成,将Header和Claim这两个Json分别使用Base64方式进行编码,生成字符串Header和Payload,然后将Header和Payload以Header.Payload的格式组合在一起形成一个字符串,然后使用上面定义好的加密算法和一个密匙(这个密匙存放在服务器上,用于进行验证)对这个字符串进行加密,形成一个新的字符串,这个字符串就是Signature。
三、JWT简单Demo
pom.xml
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.4</version>
</dependency>
测试类:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
public class JwtToken {
// private static String SECRET = "zhangsan";
public static void main(String[] args) {
/* String jwtStr = createJwt("zhangsan",1000000);
System.out.println(jwtStr);*/
String jwtStr = "eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiJ6aGFuZ3NhbiIsImlhdCI6MTYyNjUxNjAyNSwic3ViIjoiemhhbmdzYW4iLCJleHAiOjE2MjY1MTcwMjV9.I-ECDg_TBvWAo6UMYTFYWL0Ojkr2beGizY7U5cgdXcc7C2vpnHOOEp9QViq7wWHTr_MIuHZkwr1Ribh99dRNoQ";
Claims claims = verifyJwt(jwtStr);
String value = (String)claims.get("zhangsan");
String subject = claims.getSubject();
String id = claims.getId();
System.out.println("id=="+id);
}
/**
* 签发JWT
* @param name
* @param Mill
* @return
*/
public static String createJwt(String name,long Mill) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS512;
SecretKeySpec key = getKey();
long timeMillis = System.currentTimeMillis();
Date now = new Date(timeMillis);
JwtBuilder builder = Jwts.builder()
.setId(name)
.setIssuedAt(now)//签发时间
.setSubject(name) //主题
.signWith(signatureAlgorithm, key);//签名算法以及秘钥
if (Mill >= 0) {
long expireTime = timeMillis + Mill;
Date expDate = new Date(expireTime);
builder.setExpiration(expDate);//过期时间
}
return builder.compact();
}
/**
* 由字符串生成加密的key
* @return
*/
public static SecretKeySpec getKey(){
String keyString = "zhangsan123";
byte[] encode = Base64.decodeBase64(keyString);
SecretKeySpec secretKeySpec = new SecretKeySpec(encode, 0, encode.length, "AES");
return secretKeySpec;
}
/**
* 校验JWT
* @param jwtStr
* @return
*/
public static Claims verifyJwt(String jwtStr) {
SecretKeySpec key = getKey();
Claims claims = Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(jwtStr).getBody();
return claims;
}
}
应用场景:用户登录成功后,后端通过jwt产生一个token返回给客户端,客户端进行存储,以后每次访问相关资源时候,后端会有一个拦截器进行拦截,按照定义好的解密规则解密出用户信息,解密成功并且存在,则允许进行后续的操作
public class JwtInterceptor implements HandlerInterceptor{
/**
* 模拟拦截用户请求的url,并从url中读取前端传来的token,如果校验通过,则允许进行后续其他的操作
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String token = request.getHeader("authorization");
Claims claims = verifyJwt(token);
String value = (String)claims.get("zhangsan");
String subject = claims.getSubject();
String id = claims.getId();
UserService userService = SpringContextUtil.getBean("usersService");
//查询是否存在该用户
Users user = userService.findById(id);
if(user != null){
return true;
}else{
//生成token的动作是不是应该在用户登录成功的时候产生呢?
return false;
}
}
}
参考:https://blog.csdn.net/zhangcongyi420/article/details/89222307