JWT介绍

文章目录

    • JWT介绍
    • 传统跨域登陆认证的方式
    • JWT的验证方式
    • JWT的数据结构
      • header 头部
      • payload 载荷
      • signature 签名
      • base64UrlEncode 编码
    • JWT 使用
    • JWT 校验真伪
    • JWT PHP库
    • JWT Golang库
    • 总结

JWT介绍

JWT (音: jot ) 是目前最流行的跨域认证解决方案,全称JSON Web Token,基于JSON的开放标准((RFC 7519) ,以token的方式代替传统的Cookie-Session模式,用于服务器、客户端传递信息签名验证。

传统跨域登陆认证的方式

传统的登陆校验采用的是cookie-session方式,一般流程是这样的:

1、客户端使用用户名和密码请求登录。
2、服务器验证账号密码通过后,在当前对话(session)里面保存相关数据,比如用户UID、登录时间等等。
3、服务器向用户返回一个 session_id,写入用户的 Cookie。
4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。
5、服务器收到 session_id,找到之前保存的数据,由此得知用户的身份,进而判断是否已经登陆等校验操作。

这种模式的缺点是扩展性不好。单机是没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。session一般是采用文件存储的方式,这带来文件同步以及读取的问题,即使将session放入redis中,也同样会带来高流量数据读取的问题。

JWT的验证方式

JWT 采用的是Token令牌的方式,来进行校验,具体如下:

1、客户端使用用户名和密码请求登录。
2、服务器验证账号和密码通过后,服务端会签发一个 Token 返回给客户端。
3、客户端收到请求后会将 Token 缓存起来,比如放在浏览器 Cookie 中或者存储在Local Stage中,之后每次请求都会携带该 Token。
4、服务端收到请求后会验证请求中携带的 Token,验证通过则进行业务逻辑处理并成功返回数据

更直观的用图表示,如下图所示:

JWT介绍_第1张图片

这样的一个流程的优点是:

1、服务端端不保存任何 session 数据了,也就是说,服务器变成无状态了,减小了服务器开销,从而比较容易实现扩展。
2、jwt构成简单,占用很少的字节,便于传输。
3、json格式通用,不同语言之间都可以使用。

JWT的数据结构

一个JWT是长这样的:
JWT介绍_第2张图片

观察上图中JWT字符串,可以发现,它由三部分构成,以点号.字符分隔。分别是:

  • 头部(header)
  • 载荷(payload) 类似于飞机上承载的物品, 包含一些定义信息和自定义信息
  • 签证(signature)

注意,JWT 内部是没有换行的,上图只是为了便于展示,将它写成了几行。

写成一行,就是下面的这个样子:

header.payload.signature

下面对3个部分分别介绍。

header 头部

JWT的头部标识用于生成签名的算法,是一个 JSON 对象,包含了两个属性。长这个样子:

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

typ属性表示这个令牌(token)的类型(type),默认为JWT,一般不改,写死。
alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);

payload 载荷

payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。它可以由三部分组成:

官方标准的字段申明
公共的字段申明
私有的字段申明

其中,JWT 规定了7个官方标准字段,不是必须填写的。JWT为了紧凑,声明名称都用三个字符的缩写:

iss:  (issuer ) jwt签发者

sub: (subject)  jwt主题 

aud: (audience )接收jwt的一方

exp:  (expiration time) jwt的过期时间,这个过期时间必须要大于签发时间

nbf: (not before))定义在什么时间之前,该jwt都是不可用的.

iat: (issued at) jwt的签发时间

jti:(JWT ID) jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

比如,一个playload的例子,如下:

{
    "iss": "admin",          //JWT签发者
    "iat": 1535967430,        //签发时间
    "exp": 1535974630,        //过期时间
    "nbf": 1535967430,         //该时间之前不接收处理该Token
    "sub": "www.qq.com",   //主题
	"aud" => "ww.qq.com", //接收jwt的一方
    "jti": "9f10e796726e332cec401c569969e13e",   //该Token唯一标识
	"name": "John Doe", //自定义的公共字段,姓名
    "admin": true // 自定义的公共字段,管理员
}

signature 签名

signature 部分是对用密钥headerplayload 两部分进行签名,防止数据被篡改。

JWT介绍_第3张图片

具体操作如下:

key = 'secretkey'
unsignedToken = base64UrlEncode (header) + '.' + base64UrlEncode (payload)
signature = HMAC-SHA256(key, unsignedToken)

通过上面的步骤,我们依次得到了JWT的3个部分,接下来,就可以用点.进行拼接,最终得到JWT完整部分:

JWT = base64UrlEncode(header) + '.' + base64UrlEncode(payload) + '.' + base64UrlEncode(signature)

base64UrlEncode 编码

上面用到的base64UrlEncode编码,这个算法跟 Base64 算法基本类似,但是为了更好的在URL上进行传输,有这些不同:=被省略、+替换成-/替换成_

具体一个php实现 base64UrlEncode 算法的例子:

/**
   * base64UrlEncode
   * @param string $input 需要编码的字符串
   * @return string
   */
   public function base64UrlEncode(string $input)
  {
    return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
  }

JWT 使用

我们生成了JWT前面,一般有两种使用方式

1、客户端请求的时候在http头部携带 Authorization: bearer token,注意bearer后面有个空格

Authorization: Bearer 

比如:

$ curl -XPOST -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MjgwMTY5MjIsImlkIjowLCJuYmYiOjE1MjgwMTY5MjIsInVzZXJuYW1lIjoiYWRtaW4ifQ.LjxrK9DuAwAzUD8-9v43NzWBN7HXsSLfebw92DKd1JQ" -H "Content-Type: application/json" http://127.0.0.1/user -d'{"username":"user1","password":"user1234"}'

2、加在url后面,通过一个get参数传递:

curl -XGET http://127.0.0.1/user?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MjgwMTY5MjIsImlkIjowLCJuYmYiOjE1MjgwMTY5MjIsInVzZXJuYW1lIjoiYWRtaW4ifQ.LjxrK9DuAwAzUD8-9v43NzWBN7HXsSLfebw92DKd1JQ

JWT 校验真伪

服务器验证一个 jwt 的过程很简单,如下:

1、服务端收到 jwt。
2、将 header 和 payload 用密钥和对应的算法生成签名。
3、判断生成的签名和 jwt 第三部分是否一致。
4、不一致则返回错误,一致则表示 payload 内的数据可信,验证通过。

JWT PHP库

jwt逻辑很简单,也可以自己实现,也可以用公共开源的库,目前有几个库,很方便使用:

https://github.com/firebase/php-jwt

特点:非常简单,即刻上手。
下载使用:composer require firebase/php-jwt

https://github.com/lcobucci/jwt

特点:功能很全,使用链式函数增加属性,但是有点复杂。
下载使用:composer require lcobucci/jwt

JWT Golang库

目前用的gloang类库最多的是这个:

http://github.com/dgrijalva/jwt-go

特点:知名度高,广泛使用
下载使用:go get github.com/pascaldekloe/jwt

更多库和文档可以见官网: https://jwt.io/

总结

jwt 的核心就是密钥,拥有密钥就拥有生成 jwt 的权利(千万不能泄露)。payload 中的数据不是加密的,不要放敏感数据。

参考资料:

https://jwt.io/
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

你可能感兴趣的:(后端技术,jwt)