JWT入门 笔记

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JWT入门 笔记相关的知识,希望对你有一定的参考价值。

参考技术A JWT 其全称为:JSON Web Token,JWT是一个“认证规范”。 简单地说就是 JSON 在 Web 上的一种带签名的标记形式。官方的定义如下:

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

即:JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。

基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。

流程上是这样的:

这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了Access-Control-Allow-Origin: *。

JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

JWT的构成

第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).

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

完整的头部就像下面这样的JSON:



'typ': 'JWT',

'alg': 'HS256'



然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

备注:

声明加密的算法:通常直接使用 HMAC SHA256

加密算法是单向函数散列算法,常见的有MD5、SHA、HAMC。

MD5(message-digest algorithm 5)(信息-摘要算法)缩写,广泛用于加密和解密技术,常用于文件校验。校验?不管文件多大,经过MD5后都能生成唯一的MD5值

SHA (Secure Hash Algorithm,安全散列算法),数字签名等密码学应用中重要的工具,安全性高于MD5

HMAC (Hash Message Authentication Code),散列消息鉴别码,基于密钥的Hash算法的认证协议。用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。常用于接口签名验证

载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分

标准中注册的声明 (建议但不强制使用) :

公共的声明 :公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有的声明 :私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

定义一个payload:



"sub": "1234567890",

"name": "John Doe",

"admin": true

然后将其进行base64加密,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行secret组合加密,然后就构成了jwt的第三部分。

// javascriptvarencodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

varsignature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

一般是在请求头里加入Authorization,并加上Bearer标注:

fetch('api/user/1',

headers:



)

服务端会验证token,如果验证通过就会返回相应的资源。整个流程就是这样的:

1. 用户使用账号和密码发出post请求;

2. 服务器使用私钥创建一个jwt;

3. 服务器返回这个jwt给浏览器;

4. 浏览器将该jwt串在请求头中向服务器发送请求;

5. 服务器验证该jwt;

6. 返回响应的资源给浏览器。

身份认证在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO)中比较广泛的使用了该技术。 信息交换在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的。

jwt入门

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入门 笔记的主要内容,如果未能解决你的问题,请参考以下文章

JWT入门简介

jwt入门学习

JWT实战入门,

JWT快速入门

jwt入门

JWT入门案例