zoukankan      html  css  js  c++  java
  • jwt

    什么是 jwt

    jwt 全称 json  web token,是一种基于JSON的、用于在网络上安全的表示双方之间的声明。目前,jwt广泛应用在系统的用户认证方面,特别是现在前后端分离项目。

    由于http 协议是一种无状态的协议,无法对客户端的身份进行认证和唯一标识,所以需要采用其他的手段来实现。

    传统的session方案和jwt方式实现

    传统的cookie-sessio机制也是为了解决客户端的身份验证而出现的,服务器同通过保存客户端的数据,并发送给客户端一个随机字符串作为session_id,这样每次请求时服务器可以根据每个客户端的session_id来找到保存在服务器的数据,从而实现客户端的身份认证,但是这种方式在现在的使用种存在问题,例如session是保存在服务器的内存中(保证效率)的,这样会耗费大量的服务器内存资源,并且在目前的分布式的服务中,session存在与单独一台服务器中,而这个session无法与其他主机共享,已经登录的客户端请求转到其他服务器上将会丢失客户端状态。 

    上述问题的存在总的来说还是session保存到服务端,并且没有进行共享而导致的,解决的方案是使用单独的session服务,保证session信息共享,这也是常用的做法,例如使用一个redis集群作为session储存的解决方案。

    另一种解决方式则是使 jwt 的方案,它不同于将客户端的信息保存到服务器,而是通过一个jwt字符串将这些数据发送到对应的客户端,让每个客户端各自保存自己的数据,下一次请求服务器时,带上这个jwt字符串,从中获取这个字符串中获取数据即可,并且为了解决数据保存到客户端而存在被篡改的问题,jwt 使用一个算法将 数据+盐 生成一个签名添加到字符串中,这个签名只有知道盐的服务器了可以生成能被自己验证通过的签名。所以客户端想要篡改数据,就必须能够根据数据生成这个签名,也就需要服务器的这个盐。只要盐是安全的,他人就只能通过其他方式破解,这种概率是比较低的。

    jwt的组成

    jwt是一个固定格式的,以点分成三段的字符串,形式如下。

    aDJIGghOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpSDGfvaG4gRG9lSGEIiwiaDSGWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

    三个部分有各自的作用,前两部分都是可以通过BASE64反解得到明文数据的,而第三部分则是更具前两部分生成的签名。分别介绍以下三部分的内容以及生成过程。

    第一部分

    第一段被称为HEADER部分,包含固定的算法和token类型数据,然后将该数据的json字符串进行base64url编码得到。该头信息说明了这是一个JWT类型的token,并且在生成签名时候,使用的是HS256算法。

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

    第二部分

    第二部分被称为payload部分,这个部分可以包含用户的个人信息内容,用于保存用户的状态信息等,然后同样的通过base64url编码,例如可以记录用户的id,姓名等,但是不能保存敏感的信息,例如密码等,因为这部分数据可以被反解,所以等同于明文保存,如果该字符串被其他恶意用户获得,将会造成严重的安全问题。

    {
      "UserId": "1001",
      "Name": "tom",
      "iat": 1516239022
      ...
    }

    数据使用json的格式保存即可,示例中UserId和Name都是自定义的数据,而iat是JWT的保留字,该key代表了特殊的含义,即该字符串签发时的时间戳信息,同时还可以指定过期时间等信息,这些保留的关键字包括:

    iss(Issuser):代表这个JWT的签发主体;
    sub(Subject):代表这个JWT的主体,即它的所有人;
    aud(Audience):代表这个JWT的接收对象;
    exp(Expiration time):是一个时间戳,代表这个JWT的过期时间;
    nbf(Not Before):是一个时间戳,代表这个JWT生效的开始时间,意味着在这个时间之前验证JWT是会失败的;
    iat(Issued at):是一个时间戳,代表这个JWT的签发时间;
    jti(JWT ID):是JWT的唯一标识。

    第三部分

    第三部分即签名,签名由生成该字符串的服务端生成。生成的过程是将前两部分的Base64url编码后的内容通过点拼接起来,然后对其进行HS256进行加密,再进行base64编码,得到第三部分的内容。

    base64url(
        HMACSHA256(
          base64UrlEncode(header) + "." + base64UrlEncode(payload),
          your-256-bit-secret (秘钥加盐)
        )
    )

    最后将三段字符串通过 .拼接起来就生成了jwt的token。

    python实现

    了解了jwt的生成方式,即可以根据上述的方式来生成一个合法的jwt字符串,而这个功能已经有现成的模块实现,即pyjwt模块,使用该模块即可简单生成jwt字符串和验证jwt,如果需要了解具体过程,查看pyjwt源码即可。

    安装模块

    pip3 install pyjwt

    生成jwt

    import jwt
    import datetime
    from jwt import exceptions
    SALT = 'iv%x6xo7l7_u9bf_u!9#g#m*)*=ej@bek5)(@u3kh*72+unjv='
    def create_token():
        # 第一部分内容,构造header
        headers = {
            'typ': 'jwt',
            'alg': 'HS256'
        }
        # 第二部分内容 构造payload
        payload = {
            'user_id': 1, # 自定义用户ID
            'username': 'wupeiqi', # 自定义用户名
            'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=5) # 超时时间
        }

       # 第三部分,使用前两部分内容,加盐(SALT),并进行BASEurl64编码得到jwt result
    = jwt.encode(payload=payload, key=SALT, algorithm="HS256", headers=headers).decode('utf-8') return result if __name__ == '__main__': token = create_token() print(token)

    校验jwt

    服务器得到用户的jwt后,需要判断该数据的合法性,通过验证签名,确保数据没有被篡改。同时验证是否过期,验证成功后,从第二部分中提取内容即可。

    详细过程:

    • 将token分割成 headerpayloadcrypto 三部分。
    • 对第一部分header进行base64url解密,得到header

    • 对第二部分payload进行base64url解密,得到payload

    • 对第三部分crypto进行base64url解密,得到signature

    • 验证第三部分signature部分数据

      • 使用 . 拼接前两段密文。
      • 从第一段明文中获取加密算法,默认:HS256
      • 使用 加密算法 对 盐 + header + payload 进行加密,将得到的结果和signature密文进行比较。如果相同,则校验通过
    import jwt
    import datetime
    from jwt import exceptions
    def get_payload(token):
        """
        根据token获取payload
        :param token:
        :return:
        """
        try:
            # 从token中获取payload【不校验合法性】
            # unverified_payload = jwt.decode(token, None, False)
            # print(unverified_payload)
            # 从token中获取payload【校验合法性】
            verified_payload = jwt.decode(token, SALT, True)
            return verified_payload
        except exceptions.ExpiredSignatureError:
            print('token已失效')
        except jwt.DecodeError:
            print('token认证失败')
        except jwt.InvalidTokenError:
            print('非法的token')
    if __name__ == '__main__':
        token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU"
        payload = get_payload(token)

    通过jwt模块的decode方法将会自动对jwt的过期时间,数据的合法性等等进行检验(需要指定特定的参数),如果校验通过则返回payload数据。否则抛出异常。得到payload数据后,将这个字符串解析为的字典格式就能方便的提取内部的内容了。

  • 相关阅读:
    Spring框架:第八章:声明式事务
    Spring框架:第七章:AOP切面编程
    Spring框架:第六章:注解功能
    Jmeter之WebService接口测试
    Jmeter中的参数化常用的几种方式
    Jmeter之定时器
    Jmeter之断言——检查点
    Jmeter重要组件介绍(一)
    Jmeter中之各种乱码问题解决方案
    Jmeter之https请求
  • 原文地址:https://www.cnblogs.com/k5210202/p/13394179.html
Copyright © 2011-2022 走看看