jwt思惟导图,让jwt再也不难懂

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

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

可是token通常都是UUID生成的一个随机码,做为一个key使用,从缓存中获取具体的用户信息。因此通常须要一个存储介质来保存token和用户信息。在一些场景中,如单点登陆时候有点麻烦。web

有没一种更方便的方式呢?答案是有的,就是咱们今天要讲的jwt。jwt也算是一个特殊的token,不过jwt中自带了用户的相关信息,因此不须要存储介质,只须要验证签名保证安全的前提下就能够直接获取到用户的相关信息。redis

      

在讲jwt以前,咱们先回顾一下session、token的相关内容。算法

session与cookie

咱们都知道http是无状态的,因此须要某种机制来识别用户和保存用户的状态。而这个机制就是session。session是保存在服务端的,服务器经过session辨别用户,而后作权限认证等。spring

那如何才知道用户的session是哪一个?这时候cookie就出场了,浏览器第一次与服务器创建链接的时候,服务器会生成一个sessionid返回浏览器,浏览器把这个sessionid存储到cookie当中,之后每次发起请求都会在请求头cookie中带上这个sessionid信息,因此服务器就是根据这个sessionid因此key获取到具体session。数据库

       

google浏览器中查看cookie内容的方法有两个:json

(一)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长啥样子不?)

真面目:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

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

那么咱们一一把这串数据分析一下。

首先从jwt的消息结构开始分析:

jwt消息结构

jwt有3个组成部分,分别是

  • 头部(header)

  • 载荷(payload)

  • 签证(signature)

先回头看看jwt真面目那个例子。仔细点,认真点,有没在茫茫字母和数字中发现两个.(点号)。

(是的,你没有猜错!)

就是这两个点号把jwt分红了3部分,分别对应着上面说的头部,载荷,签证。

先来说讲头部:

Jwt的头部承载两部分信息:

  • 声明类型,这里是jwt

  • 声明加密的算法,一般直接使用HMACSHA256,就是HS256了

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

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

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

(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":"1234567890","name":"John Doe","iat":1516239022}

sub和iat是标准声明,分别表明所面向的用户和jwt签发时间。

从上面我知道了:

  • 这个是发给一个帐号是1234567890的用户(也许是ID)

  • 名字叫John Doe

  • 签发时间是1516239022(2018/1/18 9:30:22)

(牛逼~)

只剩下最后一部分了,待我一个闪现,外加一个大招秒杀它!

       

(emmmmm~~~,请求集合!!求救~)

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

SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

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

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

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  (个人密钥)
)

也就是说,签证部分的信息有3个组成部分:

  • 头部-header (base64后的)

  • 载荷-payload (base64后的)

  • 密钥-secret

而后HMACSHA256只有两个参数,

  • base64后的头部 + "." + base64后的载荷

  • 密钥-secret

好了,这部分我就不作代码演示了。由于有现成的工具类能够直接用,哈哈

下面咱们就介绍一下经常使用的生成jwt的工具类:

  • https://jwt.io/

以上就是官网给咱们介绍的几种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
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:

//生成token
String token = jwtUtils.generateToken(userId);

获取jwt的有效信息:

//获取载荷信息
Claims claims = jwtUtils.getClaimByToken(token);
if(claims == null || jwtUtils.isTokenExpired(claims.getExpiration())){
    throw new RRException(jwtUtils.getHeader() + "失效,请从新登陆", HttpStatus.UNAUTHORIZED.value());
}

long userId = Long.parseLong(claims.getSubject())

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

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

使用场景

闲聊一下jwt的使用场景

详细能够看看这篇文章:

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

总结

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

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

二、JWT适合一次性的命令认证,颁发一个有效期极短的JWT,即便暴露了危险也很小,因为每次操做都会生成新的JWT,所以也不必保存JWT,真正实现无状态。

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

(完)

java思惟导图

关注公众号,天天java一下,成就架构师

相关文章
相关标签/搜索