https://www.bilibili.com/video/BV1i54y1m7cP
文章目录
1. 什么是JWT2. jwt能做什么3. 为什么是JWT传统的基于session认证jwt认证流程jwt组成优缺点优点:缺点:
demo整合spring boot封装工具类JWTUtils
1. 什么是JWT
jwt(json web token)是一种开放标准(rfc 7519),它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象安全的传输信息。此信息可以验证和信任,因为它是数字签名的。jwt可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名通俗来讲,jwt就是通过JSON形式作为WEB应用中的令牌,用于在各方之间安全的将信息作为JSON对象传输。在数据传输过程中还可以完成数据加密、签名等相关处理。
2. jwt能做什么
授权 这是使用jwt最常见的方案。一旦用户登录,每个后续请求将包含JWT,从而允许用户访问该令牌允许的路由,服务和资源。单点登录是当今广泛使用JWT的一项功能,因为它的开销很小并且可以在不同的域中轻松使用。信息交换 jwt是在各方之间安全传输信息的好办法。因为可以对JWT进行签名,所以可以确保发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此还可以验证内容是否遭到篡改。
3. 为什么是JWT
传统的基于session认证
因为http本身是一种无状态的协议,我们并不能知道是哪一个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器端存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,以便引用识别用户。
暴露问题:
每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以便用户下次请求的鉴别,通常而言session是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大用户认证之后,服务端做认证记录。如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力,这也意味着限制了应用的扩展能力。因为是基于cookie来识别用户的,当cookie被截获,用户很容易受到2跨站请求伪造的攻击。在前后端分离系统中就更加痛苦:前后端分离([用户]< == > [前端web] < == > [代理层] < == > [后端应用] )在应用解耦后增加了部署的复杂性。通常用户一次请求就要转发多次。如果用session每次携带sessionid到服务器,服务器还要查询用户信息。同时如果用户很多。这些信息存储在服务器内存中,给服务器增加负担。还有就是CSRF(跨站伪造请求攻击),session是基于cookie进行用户识别的,cookie如果被截获,用户很容易受到跨站请求伪造的攻击。还有就是sessionid是一个特征值,表达的信息不够丰富。不容易扩展。入股后端应用是多节点部署,那么就要实现session共享机制。不方便集群应用。
jwt认证流程
首先,前端通过web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密传输(https协议),从而避免敏感信息被嗅探。后端核对用户名和密码成功后,将用户的id等其他信息作为JWT payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个token。后端将jwt字符串作为登陆成功的返回结果返回给前端。前端将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决xss和xsrf问题)后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查token是否过期;检查token的接收方是否是自己(可选)。验证通过后后端使用JWT包含的用户信息进行其它逻辑操作,返回相应结果。
jwt组成
jwt分为三个部分
第一部分 header :标记使用什么算法 (HS256/RSA256)
令牌的类型和所使用的签名算法,例如HMAC SHA256或RSA。{“alg”:“HS256”,“typ”:“JWT”} 第二部分 PayLoad (载荷) jwt存放的数据第三部分 PayLoad 采用MD5加密之后的签名值
! jwt不要存放重要数据 Base64.Encode(header). Base64.Encode(PayLoad ).签名值
private static final String SIGN_KEY
= "mayikt";
@Test
public void test1(){
JSONObject header
= new JSONObject();
header
.put("alg","HS256");
JSONObject payLoad
= new JSONObject();
payLoad
.put("phone","18611011111");
String payLoadStr
= payLoad
.toJSONString();
String sign
= DigestUtils
.md5DigestAsHex((payLoadStr
+SIGN_KEY
).getBytes());
Base64
.Encoder encoder
= Base64
.getEncoder();
String jwt
= (String
) encoder
.encodeToString(header
.toJSONString().getBytes())+"."+
encoder
.encodeToString(payLoadStr
.getBytes())+"."
+sign
;
System
.out
.println(jwt
);
}
生成的jwt可在https://jwt.io/验证
验证签名位true的情况下就可以获取payload数据,否则jwt无效。
优缺点
优点:
简洁:数据量小,传输速度快自包含:负载中包含了所有用户所需要的信息,避免了多次查询数据库jwt客户端就可验证,减轻服务端的压力,适合分布式微服务以JSON加密的形式保存在客户端,跨语言,原则上任何web形式都支持。jwt查询效率比token高不容易被客户端篡改数据
缺点:
一旦生成好一个jwt,后期比较难使它失效如果payload数据过多,占用服务器带宽资源。
建议在jwt的payload数据中增加时间戳,设置有效期
demo
引入依赖 com.auth0 java-jwt
<dependency>
<groupId>com.auth0
</groupId>
<artifactId>java-jwt
</artifactId>
<version>3.11.0
</version>
</dependency>
生成token:
Calendar instance
= Calendar
.getInstance();
instance
.add(Calendar
.SECOND
,90);
String token
= JWT
.create()
.withClaim("user","yz")
.withExpiresAt(instance
.getTime())
.sign(Algorithm
.HMAC256("token!Q2W#E$RW"));
System
.out
.println(token
);
解析结果:
header:
{
"typ": "JWT",
"alg": "HS256"
}
payload
: {
"exp": 1602178796,
"user": "yz"
}
根据令牌和签名解析数据
JWTVerifier jwtVerifier
= JWT
.require(Algorithm
.HMAC256("token!Q2W#E$RW")).build();
DecodedJWT decodedJWT
= jwtVerifier
.verify(token
);
System
.out
.println("user: " + decodedJWT
.getClaim("user").asString());
System
.out
.println("id: " + decodedJWT
.getClaim("id").asInt());
System
.out
.println("过期时间:" + decodedJWT
.getExpiresAt());
整合spring boot
封装工具类JWTUtils
方法 getToken 传入Map<String,Object> map foreach map -> withClaim
验证token方法 verify
获取token信息方法:getTokenInfo(String token,String key) 或直接返回DecodedJWT
连接数据库