传统的token,例如;用户登录成功生成对应的令牌,Key为令牌 格式(value: userid)
隐藏了数据真实性,同时将该token存放到redis中,返回对应的真是令牌给客户端存放
客户端每次访问后端请求的时候,会传递该token在请求中,服务器端接收到该token之后,从redis中查询如果存在的情况下,则说明在有效期内,如果在redis中不存在的情况下,则说明过期或者token错误
Jwt底层组成部分:
- 头部(header): 存放加密方式 alg:””
{
type=”jwt” 默认为jwt :意思是类型为jwt
alg:”HS256” :意思是加密算法为hs256
}
- 装载的数据(PayLoad) :携带存放的数据 用户名称 ,用户头像之类 敏感数据不建议存放(存在客户端) 标准中注册的声明(建议不强制使用)
PayLoad其中包含claims。claims是关于实体(常用的是用户信息)和其他数据的声明,claims有三种类型: registered, public, and private claims。
iss: jwt签发者
sub: jwt所面向的用户
and: 接受jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可以用的
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token.从而回避重复攻击
{
“userId”:”1234”,
“username”:”username”,
“过期时间”:“ ”
}
注意:在Payload中不能够存放敏感数,手机号码,密码
Token对应存放在redis或者数据库中的数据。
- 验证签名(verify signature); 防止篡改payloal中的数据
Jwt与token最大的区别:
Toeken依赖于Redis来查询数据信息,token存放value数据相当来说比较安全,其他人无法连接我们的redis
Jwt不需要依赖于服务器端,将数据信息内容直接存放在客户端(浏览器)
Jwt优缺点:
优点:
1.无需再在服务器存放用户的数据,减轻服务器端的压力
2.轻量级,json风格比较简单
3.跨语言
缺点:
- Token 一旦生成,后期无法修改,也就无法更新jwt有效期
- 无法销毁一个token
Base64不是加密和解密,主要是编码和解码,基于64个可打印字符表示二进制数据
程序;
坐标:
<!-- jwt坐标-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
手写jwt
public class JwtTest {
//手写jwt
public static void main(String[] args) throws Exception {
String signKey="signKey";//盐
/* 头部(header)
装载的数据(PayLoad)
验证签名(verify signature)*/
JSONObject header = new JSONObject();
header.put("alg","HS256");
header.put("typ","jwt");
JSONObject payLoad = new JSONObject();
payLoad.put("username","ysw");
payLoad.put("userage","21");
payLoad.put("userId","2");
//base64编码String jwtHeader = Base64.getEncoder().encodeToString(header.toJSONString().getBytes());
String jwtPayLoad = Base64.getEncoder().encodeToString(payLoad.toString().getBytes());
String jwtPayLoadMd5 = Md5Util.encodeByMd5(payLoad.toString()+signKey); //验证签名(verify signature)//这种可以暴力破解,我们可以加盐
String jwt=jwtHeader+"."+jwtPayLoad+"."+jwtPayLoadMd5;
String paload = jwt.split("\\.")[1];
byte[] bytes= Base64.getDecoder().decode(paload.getBytes());
String jwtPlayloadStr=new String(bytes, "UTF-8");
System.out.println("jwtPlayloadStr----"+jwtPlayloadStr);
String s = Md5Util.encodeByMd5(jwtPlayloadStr + signKey);
String payLoadMd5= jwt.split("\\.")[2];
System.out.println(s.equals(payLoadMd5));
//验证签名:防止接口参数被篡改 用md5单向加密,不可逆
}
}
整合JWT:
public static String buildJwt() {
//签名算法,选择SHA-256
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
String signKey="signKey";//盐
//获取当前系统时间
Date now = new Date(System.currentTimeMillis());
Date exp = new Date(System.currentTimeMillis()+1000*60*60);
System.out.println("exSp--------"+exp);
JSONObject payLoad = new JSONObject();
payLoad.put("username","ysw");
payLoad.put("userage","21");
payLoad.put("userId","2");
//jwt
JwtBuilder jwt = Jwts.builder()
.setClaims(payLoad)//自定义内容
.setId(UUID.randomUUID().toString()) //每个jwt的唯一标识
.setIssuedAt(now) //签发时间
.setExpiration(exp) //过期时间
.setSubject("username") //可以放用户名,用户唯一标识
.signWith(signatureAlgorithm, signKey);
String compact = jwt.compact();
System.out.println(compact);
return compact;
}
//解jwt
public static void main(String[] args) {
String jwt = buildJwt();
String signKey="signKey";//盐
Claims claims = Jwts.parser().setSigningKey(signKey)
.parseClaimsJws(jwt).getBody();
System.out.println(claims);
int Boolean = (int) claims.get("exp");
long now = System.currentTimeMillis()/1000;
long nowex =Boolean- now;
System.out.println("b---"+Boolean);
System.out.println(nowex);
}
}