zoukankan      html  css  js  c++  java
  • JWT认证方案与禁用令牌策略

    认证方案

    1.1 jwt

    • 对比状态保持机制

      • APP不支持状态保持
      • 状态保持有同源策略, 无法跨服务器传递
    • 不可逆加密

      • md5 sha1 sha256
      • 主要用于数据认证, 防止数据被修改
    • 消息摘要 MD

      • 通过哈希算法将任意长度内容转为定长内容, 且相同内容的哈希值始终相同, 不同内容的哈希值不同(极小概率出现碰撞)

      • 由于其唯一性, 一般将数据的哈希值称为数据的摘要信息, 称为数据的"指纹", 用于检测数据是否被修改

      • 代表算法 sha1 sha256 md5

      • 缺点

        • 哈希算法是公开的, 如果可以获取到明文, 就可以穷举出使用的算法
    • 消息认证 MA

      • 哈希算法基础上混入秘钥, 防止哈希算法被破解, 避免签名被伪造

      • 代表算法 hmacsha256

      • JWT一般会采用 消息认证 机制

        • 一般的web应用, 不会将秘钥交给客户端 ,也就表示客户端不会验签服务器的身份
      • 缺点

        • 一旦秘钥泄露, 仍然可以伪造签名
    • 数字签名

      • 利用非对称加密对摘要信息进行加密, 避免摘要信息被伪造

      • 非对称加密采用秘钥对

        • 公钥和私钥
        • 公钥加密, 私钥解密
        • 私钥加密, 公钥解密
        • 私钥可以推出公钥, 公钥无法推出私钥
      • 发送者使用私钥对数据摘要加密(签名), 接收者使用对应的公钥解密, 然后对数据进行哈希处理, 比对摘要信息是否一致

      • 代表算法 RSA

      • 使用场景

        • 安全级别要求比较高的系统, 如银行等
      • 优点

        • 客户端不会像消息认证一样保存秘钥, 而是保存了非对称加密的公钥, 即使客户端被破解, 公钥被获取, 也无法通过公钥生成合法的签名
      • 缺点

        • 效率低

    1.2 PyJWT

    • 安装 pip install PyJWT

      import jwt
      from datetime import datetime, timedelta
      from jwt import PyJWTError
      
      # 包装数据  jwt的规范中要求通过exp参数来设置有效期, 要求有效期使用格林尼治时间
      payload = {'payload': 'test', 'exp': datetime.utcnow() + timedelta(seconds=30)}
      
      key = 'secret'
      # # 生成jwt
      # token = jwt.encode(payload, key, algorithm='HS256')
      # print(token)
      
      
      token = b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXlsb2FkIjoidGVzdCIsImV4cCI6MTU2MjgwOTkzMn0.BSc0A2ibdjHTlmW7wtWfj5ZGkny8RX8tV12313'
      # 验证jwt    pyjwt内部对有效期进行了验证, 如果超过时间, 会报错
      try:
          ret = jwt.decode(token, key, algorithms='HS256')
          print(ret)
      except PyJWTError as e:
          print("jwt认证失败")
      
    • 数字签名

      • 使用openssl 生成RSA秘钥对

        # 生成私钥,指定私钥的长度为2048bit   1024基本安全, 2048非常安全
        openssl genrsa -out rsa_private_key.pem 2048
        # 根据私钥生成对应的公钥
        openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key_2048.pub
        # 私钥转化成pkcs8格式, 非必须,pkcs8格式解析起来更方便
        openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt > rsa_private_key_pkcs8.pem
        
        import jwt
        
        """服务器使用私钥生成签名  对数据摘要加密 称为 签名"""
        # with open('rsa_private_key_pkcs8.pem', 'rb') as f:
        #     private_key = f.read()
        #     # 生成数字签名
        #     encoded = jwt.encode({'some': 'payload'}, private_key, algorithm='RS256')
        #     print(encoded)
        
        
        """客户端用公钥验签"""
        encoded = b'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzb21lIjoicGF5bG9hZCJ9.S3gxuFvPiYk2752deTDm6qupj53S0b_-WvFZLKnWzLgDTFjFF_uiwmI6GAT1mKaNvWIyxFQ1PMUPxjkdLuJpGbN3hpHM_eKaQNm_RTvY8UUh6tvq8kpH4FAF2WOglwQK9f3nS8R73PrYhQFDHcfSEBWoJJPva_Pb3YEPMawUTPmd8aeS2uma4n9JqaZwCWm1GE-6S0lKNHp7ZWMlxb5E1R_FgSLIiE3qQq-mWsweyMRtsyCBCaB1W6Y24EYuDW0KHu6k6jGZdwwABVuwyKXKVTTf_XvxM3X41ggpY6mkarSXZsF3-Aw_jWOUBHy9VBHfPCeklur6oMfyGT4FQzkcQQ'
        
        
        with open('rsa_public_key_2048.pub', 'rb') as f:
            public_key = f.read()
            decoded = jwt.decode(encoded, public_key, algorithms='RS256')
            print(decoded)
        

    1.3 JWE

    • 可逆加密

      • 对称加密
        • 代表算法 des 3des aes
      • 非对称加密
        • 代表算法 RSA
        • 慢, 不适合大型数据加密
        • 加密时, 一般公钥加密, 私钥解密, 与签名相反
        • 一般私有只有一方持有, 公钥则可以多方持有(公钥公开)
        • 私钥唯一, 使用私钥签名, 公钥验签, 可以保证签名者身份唯一
          • 加密时, 私钥解密, 保证可以解密者唯一
        • 生成方式 openssl
      • 主要用于数据加密
    • 最佳方案JWE

      • 传输的数据使用对称加密, 生成数据密文, 对称加密秘钥是随机的
      • 为了防止数据篡改, 对数据密文进行摘要认证(一般使用消息认证), 摘要认证的秘钥也是随机的
      • 对称加密的秘钥 和 摘要认证的秘钥 使用非对称加密进行处理
      • JWE的耗时远高于JWS
      • 用于金融领域

    åˆå¯¹ç§°åŠ å¯†å†éžå¯¹ç§°åŠ å¯†

    • 安装 pip install authlib

      
      

    1.4 refresh token

    1.4.1 刷新token实现流程

    refreash_token

    • 特点
      • 访问令牌虽然使用频繁, 但是有效期短, 只有两个小时
      • 刷新令牌有效期长, 但是访问次数少, 可以减少泄露的风险
    1.4.2 登录接口
    • 接口设计

    ç™"录接口设计

    • 视图逻辑
    • 生成令牌
    1.4.3 访问控制
    • 对于所有的接口都需要获取认证信息 使用请求钩子实现
    • 对于指定的接口进行访问控制 使用装饰器
    • 请求钩子和装饰器

    [外链图片转存失败(img-zvgzlZNE-1562944375228)(Untitled.assets/image-20190711122529413.png)]

    1.4禁用令牌

    • 需求场景

      • 用户修改密码, 需要颁发新的token, 禁用还在有效期的旧token
      • 后台封禁用户
    • 逻辑

      • 禁用旧密码的令牌
      import random
      
      from redis import StrictRedis
      
      redis_client = StrictRedis()
      
      user_id = 111
      key = 'user:{}'.format(user_id)
      token = None  # 记录token
      
      
      def update_pwd():
          """修改密码"""
          print('修改完成密码')
      
          # 先删除已有的白名单
          if redis_client.exists(key):
              redis_client.delete(key)
      
          # 将修改了密码的用户记录到白名单中
          redis_client.sadd(key, 1)
          # 设置有效期
          redis_client.expire(key, 60 * 60 * 2)
      
      
      def login():
          """登录"""
          print('登录成功')
      
          # 生成新的token
          global token
          token = random.randint(100, 999)
      
          # 判断是否有该用户对应的白名单
          if redis_client.exists(key):
              # 将token加入到白名单中
              redis_client.sadd(key, token)
      
      
      def verify():
          """校验认证"""
          print('验证token成功')
          # 判断该用户是否有白名单(如果有, 说明修改过密码)
          if redis_client.exists(key):
              # 判断该token是否在白名单中
              if redis_client.sismember(key, token):
                  # 如果在, 允许访问
                  print("是新token, 允许访问")
              else:
                  # 如果不在, 重新登录
                  print('是旧token, 需要重新登录')
          else:
              print("没有修改过密码. 允许访问")
      
      if __name__ == '__main__':
          update_pwd()
          login()
          verify()
      

    参考:https://www.cnblogs.com/yblackd/p/12254209.html

  • 相关阅读:
    LR网页细分图中的时间详解
    LoadRunner系列实例之— 01录制cas登陆脚本
    Oracle 身份证校验
    Oracle中执行存储过程call和exec区别
    MD5 加密的密码在数据库重置
    python学习 (二十九) range函数
    python学习 (二十八) Python的for 循环
    二十一 守护线程
    二十 线程的优先级
    十九 yield方法
  • 原文地址:https://www.cnblogs.com/yblackd/p/14533406.html
Copyright © 2011-2022 走看看