zoukankan      html  css  js  c++  java
  • 使用python实现后台系统的JWT认证

    介绍

    JWT协议似乎已经应用十分广泛,JSON Web Token——一种基于token的json格式web认证方法。基本的原理是,第一次认证通过用户名密码,服务端签发一个json格式的token。后续客户端的请求都携带这个token,服务端仅需要解析这个token,来判别客户端的身份和合法性。JWT协议仅仅规定了这个协议的格式,主要分为三部分:
    1.header头部:
    声明类型,这里是jwt
    声明加密的算法 通常直接使用 HMAC SHA256,再将其进行base64编码
    {
    'typ': 'JWT',
    'alg': 'HS256'
    }

    2 payload载荷:
    payload是放置实际有效使用信息的地方。JWT定义了几种内容,包括:

    标准中注册的声明,如签发者,接收者,有效时间(exp),时间戳(iat,issued at)等;为官方建议但非必须
    {
    'user_id': 2342,
    'user_role': root,
    'iat': 1577255177
    }
    payload中的内容是自由的,按照自己开发的需要加入。

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

    JWT实现

    这里使用python模块itsdangerous,这个模块能做很多编码工作,其中一个是实现JWS的token序列。
    genTokenSeq这个函数用于生成token。其中使用的是TimedJSONWebSignatureSerializer进行序列的生成,这里secret_key密钥、salt值是随机uuid,当然也可以自定义。expires_in是超时时间间隔,这个间隔以秒记,可以直接在这里设置

    from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
    import time
    
    
    def genTokenSeq(name, passwd, address, expires=300):
        '''
        加密数据为jwt字符串
        :param name: 用户名
        :param passwd: 用户密码
        :param address: 服务器地址
        :param expires: 过期时间,默认5分钟,单位:秒
        :return: 返回字节流字符串
        salt:随机字符串,默认不可变,更改需要与服务器沟通
        secret_key:秘钥,默认不可变,更改需要与服务器沟通
        '''
        s = Serializer(
            salt='16fcf475-5180-4916-83c1-5ff79616eaa9',
            secret_key='4180da82-0c83-4d66-ab14-e2793573ecaa',
            expires_in=expires
        )
        timestamp = time.time()
        json_str = {
             'user_name': name,
             'user_passwd': passwd,
             'user_address': address,
             'timeout': expires,
             'iat': timestamp
        }
        return s.dumps(json_str)
    

    使用这个Serializer可以帮我们处理好header、signature的问题。我们只需要用s.dumps将payload的内容写进来。
    生成的token是这样的 eyJleHAiOjE1NzM1NDkyOTgsImFsZyI6IkhTNTEyIiwiaWF0IjoxNTczNTQ4OTk4fQ.eyJ1c2VyX2FkZHJlc3MiOjEyMCwidGltZW91dCI6MzAwLCJpYXQiOjE1NzM1NDg5OTguODI2NjY4LCJ1c2VyX3Bhc3N3ZCI6Im0iLCJ1c2VyX25hbWUiOiJ4eCJ9.ixTUKxwB6TW8K2dQMBYb6hfZpIejEv45nPL5AfVSI8A91Ec76BroXgPTaAEPfxSER5PnAghWDkaCOhcqsMRMKA

    这个是解码:
    from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
    from itsdangerous import SignatureExpired, BadSignature, BadData

    def tokenAuth(token):
        '''
        解码jwt数据
        :param token: jwt字符串
        :return: 返回解密后的数据
        '''
        s = Serializer(
            secret_key='4180da82-0c83-4d66-ab14-e2793573ecaa',
            salt='16fcf475-5180-4916-83c1-5ff79616eaa9')
        try:
            data = s.loads(token)
        except SignatureExpired:
            msg = 'token expired'
            return [None, msg]
        except BadSignature as e:
            encoded_payload = e.payload
            if encoded_payload is not None:
                try:
                    s.load_payload(encoded_payload)
                except BadData:
                    # the token is tampered.
                    msg = 'token tampered'
                    return [None, msg]
            msg = 'badSignature of token'
    
            return [None, msg]
        except:
            msg = 'wrong token with unknown reason'
            return [None, msg]
        if ('user_name' not in data) or ('user_passwd' not in data) or ('user_address' not in data):
            msg = 'illegal payload inside'
            return [None, msg]
        msg = 'user(' + data['user_name'] + ') logged in by token.'
    
        user_name = data['user_name']
        user_passwd = data['user_passwd']
        user_address = data['user_address']
    
        return [user_name, user_passwd, user_address, msg]
    

    解析需要使用到同样的serializer,配置一样的secret key和salt,使用loads方法来解析token。itsdangerous提供了各种异常处理类,用起来也很方便:

    如果是SignatureExpired,则可以直接返回过期;
    如果是BadSignature,则代表了所有其他签名错误的情况,于是又分为:
    能读取到payload:那么这个消息是一个内容被篡改、消息体加密过程正确的消息,secret key和salt很可能泄露了;
    不能读取到payload: 消息体直接被篡改,secret key和salt应该仍然安全。

    检查和判定的机制如下:

    1.使用加密的类,再用来解密(用上之前的密钥和盐值),得到结果存入data;
    2.如果捕获到SignatureExpired异常,则代表根据token中的expired设置,token已经超时失效,返回‘token expired’;
    3.如果是其他BadSignature异常,又要分为:
    3.1 如果payload还完整,则解析payload,如果捕获BadData异常,则代表token已经被篡改,返回‘token tampered’;
    3.2 如果payload不完整,直接返回‘badSignature of token’;
    4.如果以上异常都不对,那只能返回未知异常‘wrong token with unknown reason’;
    5.最后,如果data能正常解析,则将payload中的数据取出来,验证payload中是否有合法信息,如果数据不合法,则返回‘illegal payload inside’。一旦出现这种情况,则代表密钥和盐值泄露的可能性很大。

  • 相关阅读:
    AO中的GraphicsLayer---------元素的容器
    Spring中基于Java的配置@Configuration和@Bean用法
    spring注解开发AnnotationConfigApplicationContext的使用
    java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "getClassLoader")
    java.lang.ClassNotFoundException: org.apache.juli.logging.LogFactory的解决办法
    Multiple markers at this line @Override的解决方法
    springmvc-mvc:resource标签使用
    SpringMVC <mvc:view-controller path=""/>标签
    深入理解Spring MVC 思想
    解决 Eclipse 导入项目后 Maven Dependencies missing jar 问题
  • 原文地址:https://www.cnblogs.com/vinic-xxm/p/11843220.html
Copyright © 2011-2022 走看看