• 近期将进行后台系统升级,如有访问不畅,请稍后再试!
  • 极客文库-知识库上线!
  • 极客文库小编@勤劳的小蚂蚁,为您推荐每日资讯,欢迎关注!
  • 每日更新优质编程文章!
  • 更多功能模块开发中。。。

jwt思维导图,让jwt不再难懂

一般情况下,web 项目都是通过 session 进行认证,每次请求数据时,都会把 jsessionid 放在 cookie 中,以便与服务端保持会话。

前后端分离项目中,通过 token 进行认证(登录时,生成唯一的 token 凭证),每次请求数据时,都会把 token 放在 header 中,服务端解析 token,并确定用户身份及用户权限,数据通过 json 交互。

但是 token 一般都是 UUID 生成的一个随机码,作为一个 key 使用,从缓存中获取具体的用户信息。所以一般需要一个存储介质来保存 token 和用户信息。在一些场景中,如单点登录时候有点麻烦。

有没一种更方便的方式呢?答案是有的,就是我们今天要讲的 jwt。jwt 也算是一个特殊的 token,不过 jwt 中自带了用户的相关信息,所以不需要存储介质,只需要验证签名保证安全的前提下就可以直接获取到用户的相关信息。
              
在讲 jwt 之前,我们先回顾一下 session、token 的相关内容。

session 与 cookie

我们都知道 http 是无状态的,所以需要某种机制来识别用户和保存用户的状态。而这个机制就是 session。session 是保存在服务端的,服务器通过 session 辨别用户,然后做权限认证等。

那如何才知道用户的 session 是哪个?这时候 cookie 就出场了,浏览器第一次与服务器建立连接的时候,服务器会生成一个 sessionid 返回浏览器,浏览器把这个 sessionid 存储到 cookie 当中,以后每次发起请求都会在请求头 cookie 中带上这个 sessionid 信息,所以服务器就是根据这个 sessionid 所以 key 获取到具体 session。
             
google 浏览器中查看 cookie 内容的方法有两个:
(一)F12,查看具体请求链接的请求头信息
             
(二)点击浏览器输入框的认证小锁,可以查看这个域名的相关 cookie 信息。
              
涉及到集群环境得话,session 需要弄成分布式 session,从而保证多个应用的会话状态一致性。spring 项目可以使用 spring session+redis 来解决 session 共享问题。shiro 项目可以重写 redis 版 SessionDAO,把会话信息存到 redis 中实现共享。    
接下来我们再来聊聊 token。

Token

token,就是我们常说的用户身份令牌。只有涉及到受限资源的访问时候才需要身份令牌,所以,在访问开放资源时候 http 中是没有 token 的信息的,也即是说这时候会话是完全无状态的。token 的是在用户登录以后生成的。用户登录之后我们会生成一个 token 作为 key 保存用户的信息并返回给客户端。保存方式 set(token,用户信息)存储到 redis 等介质。

之后客户端发起的请求只要在请求头中附带 token 的信息就可以完成身份认证。
             

开源项目 renren-fast 采用了前后分离的机制,使用 token 来完成身份认证,并且集成了 shiro 框架,所以想实战的可以去 clone 下来玩玩~

(只能帮你到这了~)

好了,说了这么拓展知识,接下来我们进入我们的正题!jwt。


jwt 是什么

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准((RFC 7519)。该 token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。

JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。

jwt 的特点

  • 简洁(Compact): 可以通过 URL,POST 参数或者在 HTTP header 发送,因为数据量小,传输速度也很快
  • 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库或缓存。

说了这么多~
真面目:

傻眼了吧?看不懂吧,哈哈哈哈哈~~~~~~~~~

那么我们一一把这串数据分析一下。
首先从 jwt 的消息结构开始分析:

jwt 消息结构

jwt 有 3 个组成部分,分别是
  • 头部(header)
  • 载荷(payload)
  • 签证(signature)
先回头看看 jwt 真面目那个例子。仔细点,认真点,有没在茫茫字母和数字中发现两个.(点号)。
就是这两个点号把 jwt 分成了 3 部分,分别对应着上面说的头部,载荷,签证。

先来讲讲头部:

Jwt 的头部承载两部分信息:
  • 声明类型,这里是 jwt
  • 声明加密的算法,通常直接使用 HMACSHA256,就是 HS256 了

然后将头部进行base64 编码构成了第一部分:

(base64 懂吧?)

Base64 是一种用 64 个字符来表示任意二进制数据的方法
Base64 是一种任意二进制到文本字符串的编码方法,常用于在 URL、Cookie、网页中传输少量二进制数据。

Java 中可以使用java.util.Base64进行编码解码。

     

(我没骗你吧?头部就是这样来的)

然后我们看第二部分:载荷。
我依稀记得物理老师说过:直接施加在结构上的各种力,习惯上称为载荷(荷载)。

好了不吹牛了,这里是承载的意思。也就是说这里是承载消息具体内容的地方

内容又可以分为 3 中标准
  • 标准中注册的声明
  • 公共的声明
  • 私有的声明

payload-标准中注册的声明 (建议但不强制使用) :
  • iss: jwt 签发者
  • sub: jwt 所面向的用户
  • aud: 接收 jwt 的一方
  • exp: jwt 的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该 jwt 都是不可用的.
  • iat: jwt 的签发时间
  • jti: jwt 的唯一身份标识,主要用来作为一次性 token,从而回避重放攻击。

payload-公共的声明 :
公共的声明可以添加任何的信息。一般这里我们会存放一下用户的基本信息(非敏感信息)。

payload-私有的声明 :
私有声明是提供者和消费者所共同定义的声明。

需要注意的是,不要存放敏感信息,不要存放敏感信息,不要存放敏感信息!!!
因为:这里也是 base64 编码,任何人获取到 jwt 之后都可以解码!!(产品应该就不懂)

好了,请容许我 Base64 解码一下载荷部分内容到底是啥。
sub 和 iat 是标准声明,分别代表所面向的用户和 jwt 签发时间。

从上面我知道了:
  • 这个是发给一个账号是 1234567890 的用户(也许是 ID)
  • 名字叫 John Doe
  • 签发时间是 1516239022(2018/1/18 9:30:22)

(牛逼~)

只剩下最后一部分了,待我一个闪现,外加一个大招秒杀它!
              
(emmmmm~~~,请求集合!!求救~)

签证部分貌似和 Base64 没啥关系呀,那到底是啥的~


别急,先来说说签证用来干啥的,其实就是一个签名信息,使用了自定义的一个密钥然后加密后的结果,目的就是为了保证签名的信息没有被别人改过!(也就是保证 jwt 安全可用)

头部那里我们不是定义了一个加密算法么,就是它

也就是说,签证部分的信息有 3 个组成部分:
  • 头部-header (base64 后的)
  • 载荷-payload (base64 后的)
  • 密钥-secret

然后 HMACSHA256 只有两个参数,
  • base64 后的头部 + “.” + base64 后的载荷
  • 密钥-secret

好了,这部分我就不做代码演示了。因为有现成的工具类可以直接用,哈哈

下面我们就介绍一下常用的生成 jwt 的工具类:
              
以上就是官网给我们介绍的几种 java 可用的 jar 包。其中,io.jsonwebtoken是最常用的工具包。

使用步骤如下:
第一步,导入 jar 包:
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->

<dependency>    <groupId>io.jsonwebtoken</groupId>    <artifactId>jjwt</artifactId>    <version>0.9.1</version>
</dependency>
第二步:稍微封装一下,方便集成到项目中:
/** * jwt 工具类 * @author chenshun * @email sunlightcs@gmail.com * @date 2017/9/21 22:21 */
@ConfigurationProperties(prefix = “renren.jwt”)
@Component
publicclassJwtUtils{
   private Logger logger = LoggerFactory.getLogger(getClass());
   private String secret;    
   privatelong expire;    
   private String header;    
   /**     * 生成 jwt token     */    public String generateToken(long userId){        Date nowDate = new Date();        //过期时间        Date expireDate = new Date(nowDate.getTime() + expire * 1000);        
       return Jwts.builder()                .setHeaderParam(“typ”, “JWT”)                .setSubject(userId+“”)                .setIssuedAt(nowDate)                .setExpiration(expireDate)                .signWith(SignatureAlgorithm.HS512, secret)                .compact();    }    
   public Claims getClaimByToken(String token){        
   try {            
           return Jwts.parser()                    .setSigningKey(secret)                    .parseClaimsJws(token)                    .getBody();        }catch (Exception e){            logger.debug(“validate is token error “, e);            
           returnnull;        }    }    /**     * token 是否过期     * @return  true:过期     */    publicbooleanisTokenExpired(Date expiration){        
           return expiration.before(new Date());    }    
   //getter、setter  

}

生成 jwt:

获取 jwt 的有效信息:

嘿嘿、以上例子来自之前说个的 renren-fast 项目。所以呀,你还是去看看吧~

(又学了一个技能,好有成就感!)

使用场景

闲聊一下 jwt 的使用场景
       
详细可以看看这篇文章:

总结

最后的最后,再来个小总结:

1、在 Web 应用中,别再把JWT当做 session 使用,绝大多数情况下,传统的 cookie-session 机制工作得更好

2、JWT适合一次性的命令认证,颁发一个有效期极短的JWT,即使暴露了危险也很小,由于每次操作都会生成新的JWT,因此也没必要保存 JWT,真正实现无状态。


丨极客文库, 版权所有丨如未注明 , 均为原创丨
本网站采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行授权
转载请注明原文链接:jwt 思维导图,让 jwt 不再难懂
喜欢 (0)
[247507792@qq.com]
分享 (0)
勤劳的小蚂蚁
关于作者:
温馨提示:本文来源于网络,转载文章皆标明了出处,如果您发现侵权文章,请及时向站长反馈删除。

欢迎 注册账号 登录 发表评论!

  • 精品技术教程
  • 编程资源分享
  • 问答交流社区
  • 极客文库知识库

客服QQ


QQ:2248886839


工作时间:09:00-23:00