Python: Json web token、pyjwt、base64和时间有效性

一、JWT 介绍
先看一个字符串:

‘eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxODAwMSwidXNlcl9yb2xlIjoiYWRtaW4iLCJleHAiOjE1NjkwNzQ2ODIsIm5iZiI6MTM3MTcyMDkzOSwiaXNzIjoiYmlnX2Jvc3MifQ.8RiaABi35CCHqIg1acdav09Q6-V7jhxguzPPhns9liE’

上面字符串,中间有两个点分隔:

这个就是JWT的三部分。JSON Web Tokens由三个部分组成,用点(.)分隔,它们是:Header+ Payload +Signature。

其中,

(1)、头部信息主要是算法本身的简介。

(2)payload:

iss:jwt 签发者;

sub:jwt 所面向的用户;

aud:接收 jwt 的一方;

exp:jwt 的过期时间,这个过期时间必须要大于签发时间,这是一个秒数;

nbf:定义在什么时间之前,该 jwt 都是不可用的;

iat:jwt 的签发时间。

上面的标准中注册的声明中常用的有 exp 和 nbf。

(3)signature:

RS256 :是一种非对称算法, 它使用公共/私钥对: 标识提供方采用私钥生成签名, JWT 的使用方获取公钥以验证签名,私钥解密。
HS256 :是一种对称算法, 双方之间仅共享一个 密钥。优点是解密速度快,缺点是对密钥双方都有,保管风险大一点,此外是一对一。
在pyjwt库中,‘RS256’:即指 RSAAlgorithm(RSAAlgorithm.SHA256),HS256’具体是指 HMACAlgorithm(HMACAlgorithm.SHA256)。
pyjwt库中, 有几个重要的依赖库:

import hashlib
import hmac
import json

从代码中,可以看到,这两种算法参数,在加解密时的不同:

def HS256_payload(payload,key):
    key = 'secret_adadadpqeipqreiupqidfak'#这个自己设好,对称性的密钥
    encoded = jwt.encode(payload, key, algorithm='HS256')
    decoded = jwt.decode(encoded, key, algorithms='HS256') #algorithms多了s
    return decoded==payload

def RS256_payload(payload,key):
    private_key = b'PRIVATE KEY_k'
    public_key  = b'PUBLIC KEY_AQY'
    # 私钥加密
    encoded = jwt.encode(payload, private_key, algorithm='RS256')
    # 公钥解密
    decoded = jwt.decode(encoded, public_key, algorithms='RS256')#algorithms多了s
    return decoded==payload

二、从代码的角度来看JWT
1、token

header ={'alg': 'HS256',
'typ': 'JWT'
}

payload = {'user_id': 18001,
 'user_role': 'admin',
 'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=30),
 'nbf': 1371720939,
 'iss': "big_boss"
}
key = 'secret_adadadpqeipqreiupqidfak'#这个自己设好,对称性的密钥
encoded = jwt.encode(payload, key, headers=header,algorithm='HS256') 
print("encoded: ",encoded)

这个就是中间带两个点的JWT token。

token = str(encoded,encoding = "utf-8")

‘eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxODAwMSwidXNlcl9yb2xlIjoiYWRtaW4iLCJleHAiOjE1NjkwNzQ2ODIsIm5iZiI6MTM3MTcyMDkzOSwiaXNzIjoiYmlnX2Jvc3MifQ.8RiaABi35CCHqIg1acdav09Q6-V7jhxguzPPhns9liE’

2、base64。

JWT和base64有什么关系? 有,因为,其中首部和payload相应的字符串,都是base64后的结果。什么是base64?

在这里插入图片描述上面资料引自:https://www.jianshu.com/p/570c1acdd236,表示感谢!

3、首部:

import base64
header ={'alg': 'HS256',
'typ': 'JWT'
}
header_byte = bytes(json.dumps(header),encoding='utf-8')
header_encode = base64.b64encode(header_byte) #
header_encode =='eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9'
base64.b64decode(header_encode) ==header_byte
# b'{"typ":"JWT","alg":"HS256"}'

4、payload:

字符串如下:

eyJ1c2VyX2lkIjoxODAwMSwidXNlcl9yb2xlIjoiYWRtaW4iLCJleHAiOjE1NjkwNTY2MTMsIm5iZiI6MTM3MTcyMDkzOSwiaXNzIjoiYmlnX2Jvc3MifQ

这个如何得来?为什么要加两个“=”? 有意思的是,在解码时,加N个(N>=2)“==”对解码没有影响。一般情况,会多加几个 “=” 。

string ='eyJ1c2VyX2lkIjoxODAwMSwidXNlcl9yb2xlIjoiYWRtaW4iLCJleHAiOjE1NjkwNTY2MTMsIm5iZiI6MTM3MTcyMDkzOSwiaXNzIjoiYmlnX2Jvc3MifQ''
payload_byte = bytes(json.dumps(payload),encoding='utf-8')
payload_encode = base64.b64encode(payload_byte)
print(payload_encode)
str(payload_encode,encoding = "utf-8")  == string +'=='

payload_encode:

b’eyJ1c2VyX2lkIjogMTgwMDEsICJ1c2VyX3JvbGUiOiAiYWRtaW4iLCAiZXhwIjogMTU2OTA3NDY4MiwgIm5iZiI6IDEzNzE3MjA5MzksICJpc3MiOiAiYmlnX2Jvc3MifQ==’

5、signature:

是把首部和payload两部分合起来,加密的结果。

header_str = str(header_encode,encoding = "utf-8")
payload_str = str(payload_encode,encoding = "utf-8")[:-2] #去除2个“==”
key = 'secret_adadadpqeipqreiupqidfak'
join_str = header_str+ payload_str + key  # 待证实
siginature = (加密函数,HS256)(join_str)

关于pyjwt中encode的原码:

    def encode(self,
               payload,  # type: Union[Dict, bytes]
               key,  # type: str
               algorithm='HS256',  # type: str
               headers=None,  # type: Optional[Dict]
               json_encoder=None  # type: Optional[Callable]
               ):
        segments = []

        if algorithm is None:
            algorithm = 'none'

        if algorithm not in self._valid_algs:
            pass

        # Header
        header = {'typ': self.header_typ, 'alg': algorithm}

        if headers:
            self._validate_headers(headers)
            header.update(headers)

        json_header = force_bytes(
            json.dumps(
                header,
                separators=(',', ':'),
                cls=json_encoder
            )
        )

        segments.append(base64url_encode(json_header))
        segments.append(base64url_encode(payload))

        # Segments
        signing_input = b'.'.join(segments)
        try:
            alg_obj = self._algorithms[algorithm]
            key = alg_obj.prepare_key(key)
            signature = alg_obj.sign(signing_input, key)

        except KeyError:
            if not has_crypto and algorithm in requires_cryptography:
                raise NotImplementedError(
                    "Algorithm '%s' could not be found. Do you have cryptography "
                    "installed?" % algorithm
                )
            else:
                raise NotImplementedError('Algorithm not supported')

        segments.append(base64url_encode(signature))

        return b'.'.join(segments)

三、验证token

如果是token:

    decoded = jwt.decode(encoded, key,headers=header, algorithms='HS256')

如果是token:

decoded = jwt.decode(bytes(token,encoding='utf-8'), key,headers=header, algorithms='HS256')

decoded ==payload

四、验证的时效性

在payload中,

'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=30),
 'nbf': 1371720939,

这两项会验证明,如果时间过期,会无法验证。此时,验证自动就会失效,从而起到token自动认证的作用。

1、time()

import jwt
import json
import time as t

nowtime = t.time()
payload ={
           "name":"wang",
           "phone":"13787878787",
           "exp":int(nowtime + 20*1)  #60
        }
secret ="how are you?"

encoded = jwt.encode(payload,secret,algorithm='HS256')
token =str(encoded,encoding="utf-8")

t0=t.time()
for i in range(10):
    t.sleep( 5 )
    decoded =jwt.decode(token,secret,algorithm='HS256')
    print("i=>:",i," cost time:",t.time()-t0)
    print("decode:",decoded)
    assert decoded ==payload

在这里插入图片描述
2、datetime
改动部分代码,其它一样。

    nowtime = dt.datetime.now()
    exp_time = nowtime + dt.timedelta(seconds =20)
    print("exp_time:",exp_time)
    payload ={
               "name":"wang",
               "phone":"13787878787",
               "exp": exp_time
            }

结果如下:为什么?

exp_time: 2019-09-24 20:22:31.085716
i=>: 0  cost time: 4.999645709991455
decode: {'name': 'wang', 'phone': '13787878787', 'exp': 1569356551}
i=>: 1  cost time: 10.00132966041565
decode: {'name': 'wang', 'phone': '13787878787', 'exp': 1569356551}
i=>: 2  cost time: 15.001086235046387
decode: {'name': 'wang', 'phone': '13787878787', 'exp': 1569356551}
i=>: 3  cost time: 20.00347375869751
decode: {'name': 'wang', 'phone': '13787878787', 'exp': 1569356551}
i=>: 4  cost time: 25.00635576248169
decode: {'name': 'wang', 'phone': '13787878787', 'exp': 1569356551}
i=>: 5  cost time: 30.009093284606934
decode: {'name': 'wang', 'phone': '13787878787', 'exp': 1569356551}
i=>: 6  cost time: 35.00780892372131
decode: {'name': 'wang', 'phone': '13787878787', 'exp': 1569356551}
i=>: 7  cost time: 40.00885272026062
decode: {'name': 'wang', 'phone': '13787878787', 'exp': 1569356551}
i=>: 8  cost time: 45.008975982666016
decode: {'name': 'wang', 'phone': '13787878787', 'exp': 1569356551}
i=>: 9  cost time: 50.00984859466553
decode: {'name': 'wang', 'phone': '13787878787', 'exp': 1569356551}

问题是,你看到,datetime下的"exp":1569356551
与time方法的:“1569326991”
这个有很不一样。
因为,datetime的时间放在time之前,理论上因datetime的“exp”>time的“exp”;但事实上是相反的。慢了近30000秒!为什么会这样?

time0 =t.time()
time1= dt.datetime.now()
print("time=>time0:",time0)
print("datetime=>time1:",time1)
# datetime
seconds_from_1970= t.mktime(time1.timetuple())
print("datetime=> seconds_from_1970:",seconds_from_1970)
print("diff:",seconds_from_1970 -time0)

因为取整的原因,两者有差:

time=>time0: 1569330254.1146894
datetime=>time1: 2019-09-24 21:04:14.114689
time=>time0: 1569329927.7502277
datetime=> seconds_from_1970: 1569329927.0
diff: -0.750227689743042

综个所述,建议JWT有定时时,用time方法来做,会更好!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值