JWT(JSON Web Token)是一个非常轻巧的规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息,通常使用在HTTP通信过程中进行身份认证。

我们知道,HTTP通信是无状态的,客户端的请求到了服务器处理完之后是无法返回给原来的客户端的,因此需要对访问的客户端进行识别,常用的做法是通过Session机制:客户端在服务端登录成功后,服务端会生成一个sessionId返回给客户端,然后客户端会把这个sessionId存到本地浏览器的Cookie中,当客户端再向服务端发起请求就会从Cookie中取出这个sessionId并附加到请求头中。这样服务端就能够知道是哪个用户发起的请求。

Session存在的问题

1.Session是保存在服务端的,当客户访问量增加的时候,服务端就需要存储大量的Session会话,对响应性能造成影响。

2.当服务器为集群的时候,用户登录其中的一台服务器,会将Session保存到该服务器的内存中,但是当用户访问到其他服务器的时候,因为在该服务器的内存中找不到该Session,就会重新生成一个Session返回给客户端,造成分布式Session共享问题。通常会采用缓存一致性的技术来解决分布式Session的共享问题,或者是使用第三方缓存(比如Redis)来保存Session,其实也可以使用JWT来解决这个问题,也就是使用JWT替代Session。

JWT的产生与使用步骤

1.客户端通过用户名和密码登录服务器。

2.服务端对客户端进行身份验证。

3.服务端对该用户生成Token,返回给客户端。

4.客户端发起请求,需要携带该Token。

5.服务端收到请求后,首先验证Token,然后再返回数据。

6.客户端将Token保存到本地浏览器,一般也是保存到Cookie中。

这样,服务端就不需要保存Token,只需要对Token中携带的信息进行验证即可。

无论客户端访问后台的哪台服务器,只要可以通过用户信息的验证即可。

JWT的原理

JWT的原理是,服务器认证之后,生成一个JSON对象,返回给用户,就像下面这样。

{
"姓名": "张飞",
"角色": "管理员",
"到期时间": "2019年05月20日0时0分"
}

以后用户和服务器进行通信的时候都要发送这个JSON对象。服务器完全只靠这个对象认证用户信息。为了防止用户篡改数据,服务器在生成这个对象的时候,都会加上签名。这样,服务器就不需要保存任何Session会话信息了,也就是说,服务器变成无状态的了,从而比较容易实现扩展。

JWT的数据结构

实际的JWT大概就是像下面这个样子:

这是一个很长的字符串,中间用点分隔符【.】分隔成三个部分(要注意,JWT内部是没有换行的)。

JWT由三个部分组成:头部(Header)、负载(Payload)和签名(Signature)。

Header

Heade部分是一个JSON对象,是描述JWT的元数据。通常是下面这种格式的:

{
"alg": "HS256",
"typ": "JWT"
}

在上面的代码中,alg属性表示签名的算法(Algorithm),默认是HMAC SHA256(写成HS256);typ属性表示这个令牌(token)的类型(type),JWT令牌固定写为JWT。最后,将上面的JSON对象使用Base64URL算法转成字符串。

Payload

Payload部分也是一个JSON对象,用来存放实际需要传递的数据。JWT规定了7个官方字段供选用。

iss(issuer) 签发人
exp(expiration time) 过期时间
sub(subject) 主题
aud(audience) 受众
nbf(not before) 生效时间
iat(issued at) 签发时间
jti(jwt id) 编号

除了这些官方字段,也可以在这个部分定义私有字段:

{
"sub": "buybuybuy",
"name": "yanggb",
"admin": true
}

注意这个JWT部分默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

这个JSON对象也要使用Base64URL算法转成字符串。

Signature

Signature部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。

然后,使用Header里面指定的签名算法(默认是HMAC SHA256),按照下面的公式产生签名:

HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

最后,在算出签名之后,把Header、Payload、Signature三个部分拼成一个字符串,每个部分之间用点分隔符【.】分割,就可以返回给用户了。

Base64URL算法

前面提到,Header和Payload串型化的算法是Base64URL。这个算法跟Base64算法基本类似,但有一些小的不同。

JWT作为一个令牌(Token),有些场合可能会放到URL(比如api.yanggb.com/?token=xxx)。Base64有三个字符【+】、【/】和【=】,在URL里面是有特殊含义的,所以需要被替换掉:【=】号被省略、【+】号被替换成【-】、【/】被替换成【_】。这就是Base64URL算法和Base64算法的不同。

JWT的使用方式

客户端收到服务端返回的JWT,可以存储在Cookie里面,也可以存储在localStorage里面。此后,客户端每次与服务器通信,都要带上这个JWT。你可以把它放在Cookie里面自动发送,但是这样就不能跨域,所以更好的做法是放在HTTP请求的头信息Authorization字段里面。

Authorization: Bearer <token>

另一种做法是在跨域的时候将JWT放在POST请求的数据体里面。

JWT的几个特点

1.JWT默认是不加密,但是也是可以加密的。生成原始的Token以后,可以用密钥再加密一次。JWT不加密的情况下,不能将秘密数据写入JWT。

2.JWT不仅可以用于认证,也可以用于交换信息。有效地使用JWT,可以降低服务器查询数据库的次数。

3.JWT的最大缺点是由于服务器不保存Session会话状态,因此无法在使用过程中废止某个Token或者更改Token的权限。意思就是说,一旦JWT签发了,在到期之前就会始终有效,除非服务器有部署额外的处理逻辑。

4.JWT本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

5.为了减少盗用,JWT不应该使用HTTP协议明码传输,要使用HTTPS协议传输。

JWT的使用实例(Demo)

在Maven项目中添加JWT的依赖、相关依赖和插件。

<!-- JWT -->
<dependencies>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>

创建一个单元测试类。

public class JWTDemo {
// 加密的KEY
private static final String SECRET_KEY = "123456"; @Test
public void jwtTest() throws InterruptedException {
// 创建JWT
long time = System.currentTimeMillis() + 30*60*1000;
String jwt = this.buildJwt(new Date(time));
System.out.println("JWT字符串:" + jwt); // 验证token是否可用
boolean vaild = this.isJwtValid(jwt);
System.out.println("Token可用:" + vaild);
} /**
* 创建JWT
* @param exp 过期时间
* @return JWT String
*/
private String buildJwt(Date exp) {
return Jwts.builder()
.signWith(SignatureAlgorithm.HS256, SECRET_KEY) // SECRET_KEY是加密算法对应的密钥,这里使用额是HS256加密算法
.claim("name", "yanggb")
.setExpiration(exp) // expTime是过期时间
.compact();
} private boolean isJwtValid(String jwt) {
try {
// 解析JWT字符串中的数据,并进行最基础的验证
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY) // SECRET_KEY是加密算法对应的密钥,jjwt可以自动判断机密算法
.parseClaimsJws(jwt) // jwt是JWT字符串
.getBody();
System.out.println(claims);
}
// 在解析JWT字符串时,如果密钥不正确,将会解析失败,抛出SignatureException异常,说明该JWT字符串是伪造的
// 在解析JWT字符串时,如果过期时间字段已经早于当前时间,将会抛出ExpiredJwtException异常,说明本次请求已经失效
catch (SignatureException | ExpiredJwtException e) {
return false;
}
return true;
}
}

运行之后就可以在控制台看到打印出来的信息,然后可以使用JWT的解析工具解析出内容(https://jwt.io/):

这些就是JWT的入门知识。

"我以为回忆终究会慢慢搁浅,化作泡沫,消失在时光の海的边陲。可是记忆的种子却时而开花,时而凋零,告诉我们:今后无论去哪,都要勇敢。"

jwt入门的更多相关文章

  1. .net core jwt 入门记录

    从百度里搜索里搜索了很多jwt的文章,跟着文章写了一个demo,这里记录下学习过程中碰上的问题.看文章多遍,不如手工实现一次. 模板已上传到github.com:dogvane/webapi_jwt_ ...

  2. JWT入门简介

    官网:https://jwt.io/ 文档:https://jwt.io/introduction/ 目录 什么是JWT 头部(Header) 载荷(Payload) 签名(Signature) JW ...

  3. 【译】JWT(JSON Web Token) 入门指南

    JWT 入门指南 原文地址:https://blog.angular-university.io/angular-jwt/ 这篇文章是两篇手把手教你如何在Angular应用(也适用于企业级应用)中实现 ...

  4. 阶段5 3.微服务项目【学成在线】_day16 Spring Security Oauth2_12-SpringSecurityOauth2研究-JWT研究-生成私钥和公钥

    3.6.3 JWT入门 Spring Security 提供对JWT的支持,本节我们使用Spring Security 提供的JwtHelper来创建JWT令牌,校验JWT令牌 等操作. 3.6.3. ...

  5. 在gin框架中使用JWT

    在gin框架中使用JWT JWT全称JSON Web Token是一种跨域认证解决方案,属于一个开放的标准,它规定了一种Token实现方式,目前多用于前后端分离项目和OAuth2.0业务场景下. 什么 ...

  6. 【zipkin】链路追踪

    1,安装zipkin:https://zipkin.io/pages/quickstart.html 推荐使用docker去安装zipkin服务,下载安装执行都有了.缺点是下载要等待一段时间 2,使用 ...

  7. PHP 正则表达示

    PHP 正则表达示 php如何使用正则表达式 正则表达式基本元字符 #正则表达式示例 ^:匹配输入字符串开始的位置.如果设置了 RegExp 对象的 Multiline 属性,^ 还会与“\n”或“\ ...

  8. PHP 流程控制

    流程控制 if, else, elseif $a = 5; $b = 9; if ($a > $b): echo "a is bigger than b"; elseif ( ...

  9. JWT的入门案例

    1.什么是JWT? JWT全称JSON Web Token.是为了在网络应用环境键传递声明而执行的一种基于JSON的开放标准. 2.JWT的使用场景? 授权:一旦用户登录,每个后续请求将包括JWT,允 ...

随机推荐

  1. Elastic:用Docker部署Elastic栈

    安装 因为我们需要使用docker来进行安装,我们必须安装: docker:根据不同的操作系统,请按照要求安装docker.可以到网站https://docs.docker.com/去安装 docke ...

  2. django3-路由系统进阶

    1.django的url到底是什么 就是路径 ,看成django的目录 ,每个目录对应个视图函数 ,当然一个url仅能对应一个函数 2.url的格式 url(正则表达式,views函数名,参数,别名) ...

  3. Flask(Jinja2) 服务端模板注入漏洞(SSTI)

    flask Flask 是一个 web 框架.也就是说 Flask 为你提供工具,库和技术来允许你构建一个 web 应用程序.这个 wdb 应用程序可以使一些 web 页面.博客.wiki.基于 we ...

  4. LFI (local file inclusion vulnerability)本地文件包含

    代码实例: <?php $file = $_GET['file']; if(isset($file)) { include("pages/$file"); } else { ...

  5. 【转载】Android开发中巧用Activity和Fragment

    1.Activity的生命周期 1)多个Activity组成Activity栈,当前活动位于栈顶.我们先来看看各种Activity基类的类图: 当Activity类定义出来之后,这个Activity何 ...

  6. 滴滴出行开源项目doraemonkit食用指南

    版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/94 doraemonkit 功能介绍 一两周前在地铁上刷任 ...

  7. CocoPods原理

    CocoaPods 的原理是将所有的依赖库都放到另一个名为Pods的项目中, 然而让住项目依赖Pods项目, 这样,源码管理工作任务从主项目移到了Pods项目中. 1.Pods项目最终会编译成一个名为 ...

  8. docker容器跨服务器的迁移的方法

    docker的备份方式有export和save两种. export是当前的状态,针对的是容器,docker save 是针对镜像images. export 找出要备份容器的ID ? 1 2 3 [r ...

  9. qt 网络库使用介绍

    qt 网络库使用介绍 在.pro文件中,要手动添加network模块:QT += network 有三个核心类, QNetworkAccessManager: 发送get或者post请求. 用get方 ...

  10. openresty配置

    目录 环境 wsl安装 openresty安装 openssl版本问题 Lua模块安装 openresty使用 nginx常用命令 nginx.conf 配置 lua 的一些坑 OpenResty缓存 ...