前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >jwt思维导图,让jwt不再难懂

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

作者头像
java思维导图
发布2018-12-07 17:42:51
8610
发布2018-12-07 17:42:51
举报
文章被收录于专栏:java思维导图java思维导图

一般情况下,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下来玩玩~

  • https://gitee.com/renrenio/renren-fast

(只能帮你到这了~)

好了,说了这么拓展知识,接下来我们进入我们的正题!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的工具类:

  • https://jwt.io/

以上就是官网给我们介绍的几种java可用的jar包。其中,io.jsonwebtoken是最常用的工具包。

使用步骤如下:

第一步,导入jar包:

代码语言:javascript
复制
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

第二步:稍微封装一下,方便集成到项目中:

代码语言:javascript
复制
/**
 * jwt工具类
 * @author chenshun
 * @email sunlightcs@gmail.com
 * @date 2017/9/21 22:21
 */
@ConfigurationProperties(prefix = "renren.jwt")
@Component
public class JwtUtils {
    private Logger logger = LoggerFactory.getLogger(getClass());
    private String secret;    
    private long 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);            
            return null;
        }
    }    /**
     * token是否过期
     * @return  true:过期
     */
    public boolean isTokenExpired(Date expiration) {        
            return expiration.before(new Date());
    }    
    //getter、setter  

}

生成jwt:

获取jwt的有效信息:

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

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

使用场景

闲聊一下jwt的使用场景

详细可以看看这篇文章:

  • https://baijiahao.baidu.com/s?id=1598976581711450442&wfr=spider&for=pc

总结

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

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

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

至此,我想说的都说完了,我是吕一明,欢迎关注我公众号:java思维导图。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-11-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java思维导图 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • session与cookie
  • Token
  • jwt的特点
  • jwt消息结构
  • 使用场景
  • 总结
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档