python基於Json Web Token做服務端使用者認證

weixin_34007291發表於2018-08-14

1.python基於Json Web Token做服務端使用者認證:

Json Web Token的官方文件地址:https://jwt.io/introduction/,有需要的自行查閱

2.Json Web Token的官方定義:

JSON Web Token(JWT)是一個開放標準(RFC 7519),它定義了一種緊湊且獨立的方式,可以在各方之間作為JSON物件安全地傳輸資訊。此資訊可以通過數字簽名進行驗證和信任。 JWT可以使用祕密(使用HMAC演算法)或使用RSA或ECDSA的公鑰/私鑰對進行簽名。

3.Json Web Token的使用場景:

授權(Authorization) 資訊交換(Information Exchange)

4.Json Web Token的組成:

JSON Web Tokens由三個部分組成,用點(.)分隔,它們是:Header Payload Signature
1.Header:由兩部分組成:令牌的型別,即JWT,以及正在使用的雜湊演算法,例如HMAC SHA256或RSA
例如:這個JSON被編碼為Base64Url,形成JWT的第一部分
        {
             “alg”:“HS256”,
             “typ”:“JWT”
        }
2.Payload:有效負載,其中包含: registered claims public claims private claims
Registered claims:一組預定義的宣告,不是強制性的,但建議使用。如 iss exp sub aud
Public claims:新增自定義的資訊,一般新增使用者的相關資訊與其他業務需要的必要資訊,此部分可在客戶端解密
Private claims:使用方之間共享資訊而建立的自定義宣告,為base64是對稱解密的,也可解密
例如:這個JSON被編碼為Base64Url,形成JWT的第二部分
    {
         "iat": 1468148739, ##簽發的(UNIX時間),可選
         "exp": 1478582461, ##過期的(UNIX時間),可選
         "authos_id":"10003",
         "user_name":"157xxxx4505",
         "pass_word":"9a2aac6349b0cd0af564fdb9ffe9e9b5"
    }
3.Signature:採用encoded Header,encoded Payload和SECREATE_KEY(一般是隨機生成的),通過指定的演算法(HMACSHA256),並對其進行加密得到的簽名字串
例如:這個JSON被編碼為Base64Url,形成JWT的第三部分
    HMACSHA256(
          base64UrlEncode(header) + "." +
          base64UrlEncode(payload),
          SECREATE_KEY
    )  ##是一段重要的敏感資訊,只能在服務端解密

4.將Header Payload Signature用.拼接成一定的字串,如:

    "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRob3NfaWQiOiIxMDAwMiIsInVzZXJfbmFtZSI6IjE1NzE3OTE0NTA1IiwiZXhwIjoxNTM0MTY2MDY1Ljg0NjM1OX0.rnCiBKhy17Lb0vxpSZPuGs9-uAu1zNvFjIm-jV62rA8"

使用Json Web Token進行通訊:

在redisConn.py檔案中,定義一些基本的配置資訊:
# jwt配置
jwt_cnf = {
    "key_len": 8,
    "token_key": "tk_"
}
# redis配置,用於set(token, SECREATE_KEY)
import redis
redis_pool_configs = {
    "host": "127.0.0.1",
    "port": 6379,
    "pool_size": 5, # 0表示不使用連線池 最大連線數
    "user_name": "",
    "password": "",
    "db_name": "fileStore"
}
def engine(redis_config):
    configs = dict()
    configs['host'] = redis_config['host']
    configs['port'] = redis_config['port']
    configs['max_connections'] = redis_config['pool_size']
    pool = redis.ConnectionPool(**configs)
    if redis_config['password'] == '':
    r = redis.StrictRedis(connection_pool=pool)
    else:
    r = redis.StrictRedis(connection_pool=pool, password=redis_config['password'])
    return r
# token有效時間配置
ex_time = {
    'token_ex': 1
}
# redis
rdc = engine(redis_pool_configs)
在testEnCode.py檔案中,處理相應的邏輯:
import jwt
import string
import random
import time
from redisConn import jwt_cnf,ex_time
from redisConn import rdc as redis_service
def generate_key():
    #生成長度為8位祕鑰字串
    base_str = string.digits + string.ascii_letters
    key_list = [random.choice(base_str) for i in range(jwt_cnf['key_len'])]
    key_str = "".join(key_list)
    return key_str
def create_token(string authos_id,string user_name,string pass_word):
    payload = {
        "authos_id": authos_id,
        "user_name": user_name,
        "pass_word": pass_word,
        "exp":time.time()+ex_time['token_ex'],
        "iat":time.time()
    }
    key_pix = generate_key()  #生成長度為8位祕鑰字串
    #生成token字串
    token = jwt.encode(payload, jwt_cnf['token_key'] + key_pix, algorithm='HS256')
    redis_service.set(token, jwt_cnf['token_key'] + key_pix)
    return True, {'access_token': token, 'authos_id': authos_id}
def check_token(token):
    token_key_pix = redis_service.get(token)
    try:
        # 通過祕鑰解密,生成payload
        payload = jwt.decode(token,token_key_pix, algorithms=['HS256'])
        if payload:
            return True, payload
        return False, token
    except:
        # 如果發生異常,則回滾
        info = sys.exc_info()
        if info[0]:
            return False, "token失效"
if __name__ == '__main__':
    #程式碼測試
    bool,tokenJson = create_token("10003","157xxxx4505","9a2aac6349b0cd0af564fdb9ffe9e9b5")
    print(tokenJson.get("access_token").decode())
    #eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRob3NfaWQiOiIxMDAwMyIsInVzZXJfbmFtZSI6IjE1N3h4eHg0NTA1IiwicGFzc193b3JkIjoiOWEyYWFjNjM0OWIwY2QwYWY1NjRmZGI5ZmZlOWU5YjUiLCJpYXQiOjE1MzQyNTY2MTYuNTkxMjY5LCJleHAiOjE1MzQyNTY2MTcuNTkxMjY5fQ.ydWFF_O60vRk6U0kjlGI_0_8fD41qvZ-yF1DBxIVdTQ
    print(check_token(tokenJson.get("access_token").decode()))
    #(True, {'authos_id': '10003', 'user_name': '157xxxx4505', 'pass_word': '9a2aac6349b0cd0af564fdb9ffe9e9b5', 'iat': 1534256616.591269, 'exp': 1534256617.591269})

5.總結:

1.token生成後,通過登入介面返回給客戶端,然後客戶端需在用到使用者資訊的api介面的請求頭帶上token
2.python服務端取到請求頭中的token,在攔截器中驗證token是否失效,做相應的處理
3.mac下安裝redis教程:https://www.cnblogs.com/feijl/p/6879929.html
4.下圖是client 使用 JWT 與server 互動過程:
1927676-18fd14db87f67fc3.png
token_encode.png

相關文章