基于python的json web tokens(jwt)加密认证实现

《使用python来实现json web tokens加密协议》

这两天是北京很冷,远在南方的广东都在下雪,这个冷可想而知了…  

正体开始,我自己在尝试写一个单点登录的小系统,里面权限控制有用到jwt (json web tokens)安全策略,对于jwt,我以前专门写过一篇文章来描述他是怎么一回事, 有兴趣的朋友再瞅瞅.    这次就直接讲解在python下的json web token的实现.  不看rfc协议文档,后面直接看源码更直接一点. 


文章写的不是很严谨,欢迎来喷,另外该文后续有更新的,请到原文地址查看更新。

http://xiaorui.cc/2016/01/24/%E5%9F%BA%E4%BA%8Epython%E7%9A%84json-web-tokensjwt%E5%8A%A0%E5%AF%86%E8%AE%A4%E8%AF%81%E5%AE%9E%E7%8E%B0/

这里额外废话聊聊JWT认证的好处:

跨域: cookies在跨域场景表现并不好。基于Token的方法允许你向任何不同域名的服务器发送Ajax请求.

无状态(或者 服务端可扩展):无须再存储Session,由于Token已经自包含了所有的用户信息。

解耦:无须被绑定在一个特定的验证方案 , 只需要得知是啥加密手法及密钥就可以加密解密。

跨站点脚本攻击:由于没有基于cookie技术,你不再需要考虑跨站点请求的安全性问题。

性能:HMACSH256算法的速度还是很利索的. 在前端下具体的表现性能我没有测过.

JWT格式

JWT是一段被base64url编码过的字符序列,并用点号分隔。它由三部分组成,头部header、载荷playload与签名sign。

另外,Base64是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程。

下面是一个JWT头部 Header.  (这里是typ,不是type… 前些日子我测试jwt协议时遇到这个小坑.)

 { 
 “typ”: “JWT”, 
 “alg”: “HS256” 
 } 

下面是一个JWT的消息体 Playload
 { 
 “blog”: “xiaorui.cc”
 } 

然后把header和playload分别使用base64url编码,接着我们用’ . ‘ 把两个编码后的字符串连接起来,再把这拼接起来的字符串配合密钥进行HMAC SHA-256算法加密,最后再次base64编码下,这就就拿到了签名.   最后把header , playload, 签名用’ . ‘ 连接起来就生成了整个JWT .


请多理解下下面这图.



老规矩,通过学习jot代码看jwt是如何实现的….   大家理解了jwt生成方法就很容易自己构建一个jwt生成器了.   

#blog: xiaorui.cc

def encode(payload, signer=None, encrypter=None):
    if signer and encrypter:
        raise SignAndEncryptError()

    headers = {'typ': 'JWT', 'alg': 'none'}
    if signer:
        #更新你所选用的算法,一般都是hs256
        headers.update(signer.headers)
    if encrypter:
        headers.update(encrypter.headers)

    #头部序列化
    headers_json = json.dumps(headers, separators=(',', ':'))

    #消息主体序列化
    payload_json = json.dumps(payload, separators=(',', ':'))

    #使用base64来编码
    header_b64 = b64encode(headers_json)
    payload_b64 = b64encode(payload_json)

    first_part = header_b64
    second_part = payload_b64
    third_part = ''

    if signer:
        #jwt 签名的生成方式. 他会把header playload的base64url编码加密后再次base64编码.
        third_part = b64encode(signer.sign(first_part + '.' +
                                                second_part))
    if encrypter:
        pass  # TODO
    
    #返回可用的JWT
    return first_part + '.' + second_part + '.' + third_part


def b64encode(data):
    return base64.urlsafe_b64encode(data).rstrip('=')


#使用hmac来加密

class HmacSha(JwsBase):
    def sign(self, signing_input, key=None):
        if not key:
            key = self.key
        if not key:
            raise KeyRequiredException()
        return hmac.new(key, signing_input, self.digestmod).digest()

那么如何验证这个签名是否有效,也就是没有被篡改过?  他的原理也很简单,就是拆分这个JWT,用’ . ‘来拆分出Header , Playload, Sign,然后用base64 decode解码。  第一是判断你根据密钥针对数据加密是否拿到的是同一个签名,第二是判断签名是否过期.

我这里看的是python jot这个库,貌似关注该项目的人不是很多. 

#blog: xiaorui.cc

In [1]: from jot import jwt

In [2]: msg = jwt.encode({'status': 'ready'})

In [3]: msg
Out[3]: 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdGF0dXMiOiJyZWFkeSJ9.'

In [4]: jwt.decode(msg)
Out[4]:
{'headers': {u'alg': u'none', u'typ': u'JWT'},
 'payload': {u'status': u'ready'},
 'valid': True}

In [5]:

In [5]: from jot import jwt, jws

In [6]: msg = jwt.encode({'status': 'ready'}, signer=jws.HmacSha(
   ...: bits=256, key='verysecret', key_id='client1'))

In [7]: msg
Out[7]: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImNsaWVudDEifQ.eyJzdGF0dXMiOiJyZWFkeSJ9.DcKKQXXUjGP7pape8BgQ3AcQSPH8toWFLY2woIVUZ-w'

In [8]: jwt.decode(msg, signers=[jws.HmacSha(bits=256, key='verysecret')])
Out[8]:
{'headers': {u'alg': u'HS256', u'kid': u'client1', u'typ': u'JWT'},
 'payload': {u'status': u'ready'},
 'valid': True}

明确的说一点.

payload 的值并没有被加密,就算不知道 secert_key 的值也可以得到 payload 的值。那么签名里的secert_key的意思在于校验是否被篡改数据。

Json Wen Token的实现本来就没啥难度,也就没啥太深聊的了.  需要再次说明的是JWT不能保护数据,但是能防串改,这意思是说,我抓到你非https的jwt包,我是可以看到Header及Playload消息信息的, 如果我抓到了你cookie里的密钥的话,也是可以伪造JWT的。 但话说回来,如果我能嗅觉你的包,你用啥加密也防不住.  还是强烈大家上HTTPS,还是能挡住一些手段的. 


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc

发表评论

邮箱地址不会被公开。 必填项已用*标注