JWT(Json Web Token)
什么是JWT(Json网络令牌)
JWT(JSON 网络令牌)是一个开源的标准(RFC 7519)。这个标准定义了一个简洁并且自包含的方法,以便在系统各个部分之间安全地传送JSON对象格式的信息。因为通过JWT传递的信息是被数字签名过的,所以是可以被证实和信任的。我们可以使用单一**来加密 JWT 的签名,比如 HMAC 算法;也可以使用 RSA 的公私**对来加密 JWT 的签名。
让我们更深入地来解释一下这个定义中的一些概念:
- 简洁:因为 JWT 体积比较小,我们可以通过 URL,POST参数或者 HTTP 报头来发送 JWT。另外,较小的体积意味着发送速度比较快。
- 自包含:装载的数据包含和用户相关的所有需要的信息,避免了额外再查询一次数据库的需求。
什么时候使用JWT
- 身份验证:这是 JWT 最常用的场景。一旦用户登录进系统,每一个后继的请求都会包含 JWT。系统允许用户访问那些令牌有权限的路由,服务和资源。单点登录是一个在当代广泛使用 JWT 的特性,这是因为下面两点原因:其一是 JWT 的系统开销很小;其二是 JWT 能够轻而易举地应用到跨域的场景中。
- 信息交换:JWT 是一种在系统各个部分之间安全地传送信息的方法。这是因为只要 JWT 可以被数字签名,比如使用公私**对,你就可以信任 JWT 中包含的发送者身份信息。除此之外,当签名在计算的时候,使用了 JWT 的头部部分和负载部分,你也可以验证信息的内容有没有被篡改过。
与Session区别
- Session 通过 cookie 传输,存储在服务器,服务器通过 cookie 中的 sessionID 获取当前会话用户,对于单台服务器没问题,但多服务器就涉及到共享 Session ,而且认证用户多时 Session 会占用大量服务器内存
- JWT 存储在客户端,服务器不需要存储 JWT ,JWT 里有用户 ID,服务器拿到 JWT 后验证可以获得用户信息,也就实现了 Session 功能,但是是无状态的,只要签名秘钥足够安全就能保证 JWT 可靠性
JWT下发流程
- 用户使用用户名/密码,向认证服务器请求。
- 认证服务器认证通过后,生成JWT token返回给用户
- 用户向应用服务器调用API请求资源时都需添加JWT token
- 应用服务器接收请求后先取出JWT token进行校验,通过后返回有效资源,否则返回错误。
JWT何时下发
- 登录下发新token,原token实际上并没有失效,保证多端登录没问题.
- Token剩余有效时长大于可续期时长(根据业务平台自己定)时重新下发.举个栗子针对WEB端Token24小时有效,当有效时间小于12小时刷新Token,也就是当用户连续12小时没操作网站才会被退出.
- 修改或者重置密码时下发新Token,并吊销之前的Token
JWT吊销
JWT不需要在服务端存储,因此吊销是个大问题,无法吊销的话就会出现用户密码被盗,即使用户修改了密码,其他人也并不会立即失效,这点在安全性很高的地方几乎是不允许的情况.因此吊销是必要的.
JWT的结构
JWT包含了使用.分隔的三部分:
- Header 头部
- Payload 负载
- Signature 签名
其结构看起来是这样的
Header.Payload.Signature
Header
jwt的头部承载两部分信息:
- 声明类型,这里是jwt
- 声明加密的算法,通常直接使用 HMAC SHA256
完整的头部就像下面这样的JSON:
{
"typ": "JWT",
"alg": "HS256"
}
接下来对这部分内容使用 Base64Url 编码组成了JWT结构的第一部分
Payload
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分:
- 标准中注册的声明
- 公共的声明
- 私有的声明
标准中注册的声明 (建议但不强制使用) :
- iss: jwt签发者
- sub: jwt所面向的用户
- aud: 接收jwt的一方
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- nbf: 定义在什么时间之前,该jwt都是不可用的.
- iat: jwt的签发时间
- jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
公共的声明 :
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解码.
私有的声明 :
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为是base64编码,意味着该部分信息可以归类为明文信息。
定义一个payload:
{
"name":"demo",
"age":"28"
}
上述的负载需要经过Base64Url编码后作为JWT结构的第二部分。
Signature
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
- header (base64后的)
- payload (base64后的)
- secret
创建签名需要使用编码后的header和payload以及一个秘钥,使用header中指定签名算法进行签名。
例如如果希望使用HMAC SHA256算法,那么签名应该使用下列方式创建:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
签名用于验证消息的发送者以及消息是没有经过篡改的。 完整的JWT 完整的JWT格式的输出是以. 分隔的三段Base64编码,与SAML等基于XML的标准相比,JWT在HTTP和HTML环境中更容易传递。
下列的JWT展示了一个完整的JWT格式,它拼接了之前的Header, Payload以及秘钥签名:
**secret是保存在服务端的,服务端会根据这个**进行生成token和验证,所以需要保护好。
JAVA实现JWT
添加依赖
<!-- jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.3.0</version>
</dependency>
<!-- 时间处理,不是必须的,这里只是为了方便时间处理 -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
java代码
package jwt;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import org.joda.time.DateTime;
import org.junit.Test;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
/**
*
* @ClassName: TokenTest
* @Description: 测试
* @author cheng
* @date 2018年4月11日 下午3:34:41
*/
public class TokenTest {
/**
* 自定义**
*/
private String secret = "AHUT";
@Test
public void testToken() throws IllegalArgumentException, JWTCreationException, UnsupportedEncodingException {
/**
* 头部
*/
Map<String, Object> map = new HashMap<>();
map.put("typ", "JWT");
map.put("alg", "HS256");
/**
* 当前时间
*/
DateTime now = DateTime.now();
String token = JWT.create()
.withHeader(map)
.withClaim("name", "rick")
.withClaim("age", "23")
.withExpiresAt(now.plusSeconds(10).toDate())
.withIssuedAt(now.toDate())
.sign(Algorithm.HMAC256(secret));
System.out.println(token);
}
@Test
public void testToken1() throws IllegalArgumentException, UnsupportedEncodingException {
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoicmljayIsImV4cCI6MTUyMzQzNTY0NSwiaWF0IjoxNTIzNDM1NjM1LCJhZ2UiOiIyMyJ9.Lk5e1K9HmumBM4auM-Y7UX-_miMCvnO7R_f8A547EGI";
JWTVerifier build = JWT.require(Algorithm.HMAC256(secret)).build();
DecodedJWT jwt = null;
try {
jwt = build.verify(token);
} catch (Exception e) {
e.printStackTrace();
System.out.println("凭证已经过期");
}
Map<String, Claim> map = jwt.getClaims();
for (String key : map.keySet()) {
Claim claim = map.get(key);
System.out.println(key + ":" + claim.asString());
}
}
}
JWT优点
- 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
- 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
- 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
- 它不需要在服务端保存会话信息, 所以它易于应用的扩展
JWT安全
- 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。
- 保护好secret私钥,该私钥非常重要。
- 建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。
上一篇: JWT的原理及其数据结构
下一篇: Android中GPS定位的用法实例
推荐阅读
-
c# JSON返回格式的WEB SERVICE
-
php实现JWT(json web token)鉴权实例详解
-
ASP.NET Core 2.2 : 二十六. 应用JWT进行用户认证及Token的刷新
-
Django+JWT实现Token认证的实现方法
-
从零开始搭建前后端分离的NetCore2.2(EF Core CodeFirst+Autofac)+Vue的项目框架之七使用JWT生成Token(个人见解)
-
基于JWT的Token登录认证
-
JSON Web Token入门教程
-
松软科技Web课堂:JavaScript JSON
-
JSON WEB TOKEN(JWT)的分析
-
php JWT在web端中的使用方法教程