JWT:
JWT简介:
- JWT是json web token的缩写,它将用户信息加密到token里,服务器不保存任何用户信息,服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证
- 优点是在分布式系统中,很好地解决了单点登录问题以及session共享的问题
- 缺点是无法作废已颁布的令牌/不易应对数据过期
JWT的结构:
1、JWT长什么样
JWT是由三段信息构成的,将这三段信息文本用 . 链接一起就构成了JWT字符串
2、JWT的构成
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload,类似于飞机上承载的物品),第三部分是签证(signature)
header:
jwt的头部承载两部分信息:
声明类型,这里是jwt
声明加密的算法,通常直接使用 HMAC SHA256
完整的头部就像下面这样的JSON:
{
'typ':'JWT',
'alg':'HS256'
}
然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分
payload:
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
- 标准中注册的声明
- 公共的声明
- 私有的声明
标准中注册的声明(建议但不强制使用):
- iss:jwt签发者
- sub:jwt所面向的用户
- aud:接收jwt的一方
- exp:jwt的过期时间,这个过期时间必须要大于签发时间
- nbf:定义在什么时间之前,该jwt都是不可用的
- iat:jwt的签发时间
- jti:jwt的唯一身份标识,主要用来作一次性token,从而回避重放攻击
公共的声明:
公共的声明可以添加任何的信息,一般添加用户相关信息或其他业务需要的必要信息,但不建议添加敏感信息,应为该部分在客户端可解密
私有的声明:
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息
定义一个payload:
{
"sub":"1234567890",
"name":"John Doe",
"admin":true
}
然后将其进行base64加密,得到JWT的第二部分
signature:
jwt的第三部分是一个签证信息,这个签证信息由三部分组成
- header(base64后的)
- payload(base64后的)
- secret
这个部分需要base64加密后的header和base64加密后的payload使用,连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分
//javascriptvar encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString,'secret');
将这三部分用 . 连接成一个完整的字符串,构成了最终的jwt
JWT的应用:
一般是在请求头里加入Authorization或(xx_token),并加上xx标注:
$.ajax({
type:'post',
url:"http://localhost:8080/info",
headers:{
token:localStorage.getItem("Authorization")//获取token
}
})
服务端会验证token,如果验证通过就会返回相应的资源。整个流程就是这样的:
开发依赖包:
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
- JWTUtils.java:
package com.ahnu.utils;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JWTUtils {
private static final String SECRET = "#^%*hello";
//生成token
public static String generateToken(String acct) {
Map<String,Object> claims = new HashMap<String,Object>();
claims.put("acct",acct);
JwtBuilder jwtBuilder = Jwts.builder();
// 签发算法,设置密钥
jwtBuilder.signWith(SignatureAlgorithm.HS256,SECRET);
// body数据,要唯一,自行设置
jwtBuilder.addClaims(claims);
// 设置签发时间
jwtBuilder.setIssuedAt(new Date());
// 设置过期时间 一天
jwtBuilder.setExpiration(new Date(System.currentTimeMillis()+1000*60*60*24));
String token = jwtBuilder.compact();
return token;
}
//解密token
public static Map<String,Object> parseToken(String token) {
Jwt jwt = Jwts.parser().setSigningKey(SECRET).parse(token);
Map<String,Object> claims = (Map<String, Object>) jwt.getBody();
return claims;
}
public static void main(String[] args) {
String token = JWTUtils.generateToken("admin");
System.out.println(token);
Map<String, Object> map = JWTUtils.parseToken(token);
String acct = (String)map.get("acct");
System.out.println(acct);
}
}
总结:
- 因为json的通用性,所以JWT是可以进行跨语言支持的,像Java,JavaScript,Node JS,PHP等很多语言都可以使用
- 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息
- 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的
- 它不需要在服务端保存会话信息,所以它易于应用的扩展