sanic-jwt 的使用

Sanic 是基於 Python 的一個支援高並發的非同步 web 框架,sanic-jwt 則是針對Sanic 開發的一個基於 PyJWT 封裝的 JWT 授權認證模組。

 

sanic-jwt

 

安裝

pip install sanic-jwt

 

實例

下面實例主要總結:

  • 常用參數的配置;
  • 修改發生異常時返回的響應數據;
  • 解析和修改payload;
  • 查找用戶;

 

from sanic import Sanic, request, response
from sanic_jwt import initialize, Configuration, Responses, protected, exceptions, Authentication, inject_user


class User:

    def __init__(self, uid, username, sex, password, info, black_level=0):
        self.user_id = uid
        self.sex = sex
        self.username = username
        self.password = password
        self.personal_info = info  # 只能登錄後個人可見的資訊
        self.black_level = black_level  # 黑名單等級,默認0為正常用戶

    def __repr__(self):
        return "User(id='{}')".format(self.user_id)

    def to_dict(self):

        return {
            "uid": self.user_id,  # 注意:此處 "uid" 要與 MyJWTConfig 中的 user_id 設置一致!
            "sex": self.sex,
            "username": self.username,
            "personal_info": self.personal_info
        }


# 模擬一個用戶列表
users = [
    User(1, "user1", "", "123",  "這是僅 user1 可見資訊", 1),
    User(2, "user2", "", "456",  "這是僅 user2 可見資訊", 0)
]

username_table = {u.username: u for u in users}
userid_table = {u.user_id: u for u in users}


async def authenticate(req: request.Request):
    username = req.json.get("username", None)
    password = req.json.get("password", None)

    if not username or not password:
        raise exceptions.AuthenticationFailed("用戶名或密碼為空!")
    user = username_table.get(username, None)
    if user is None:
        raise exceptions.AuthenticationFailed("用戶名或密碼不正確!")
    if password != user.password:
        raise exceptions.AuthenticationFailed("用戶名或密碼不正確!")
    return user


class MyJWTConfig(Configuration):
    # -------------- url_prefix ---------------------
    # [描述] 獲取授權的路由地址
    # [默認] '/auth'
    url_prefix = '/login'

    # -------------- secret -------------------------
    # [描述] 加密密碼
    # [默認] 'This is a big secret. Shhhhh'
    # [建議] 該密碼是 JWT 的安全核心所在,需要保密,盡量使用更長更複雜的密碼
    secret = ',$FCyFZ^b16#m:ragM#d-!;4!U5zgZDF(EhswOL_HGV#xN1Ll%MaBU42AN=jXgp7'

    # -------------- expiration_delta ----------------------
    # [描述] 過期時間,單位為秒
    # [默認] 30 分鐘,即:60 * 30
    # [建議] 該時間不宜過長,同時建議開啟 refresh_token_enabled 以便自動更新 token
    expiration_delta = 60 * 60  # 改為 10 分鐘過期

    # -------------- cookie_set ---------------------
    # [描述] 是否將獲取到的 token 資訊寫入到 cookie
    # [默認] False,即不寫入cookie
    # 只有該項為 True,其它 cookie 相關設置才會起效。
    # cookie_set = True

    # -------------- cookie_access_token_name ---------------
    # [描述] cookie 中存儲 token 的名稱。
    # [默認] 'access_token'
    # cookie_access_token_name = "token"

    #  -------------- cookie_access_token_name ---------------
    # [描述] 包含用戶 id 的用戶對象的鍵或屬性,這裡對應 User 類的用戶唯一標識
    # [默認] 'user_id'
    user_id = "uid"

    claim_iat = True  # 顯示簽發時間,JWT的默認保留欄位,在 sanic-jwt 中默認不顯示該項


class MyJWTAuthentication(Authentication):

    # 從 payload 中解析用戶資訊,然後返回查找到的用戶
    # args[0]: request
    # args[1]: payload
    async def retrieve_user(self, *args, **kwargs):
        user_id_attribute = self.config.user_id()
        if not args or len(args) < 2 or user_id_attribute not in args[1]:
            return {}
        user_id = dict(args[1]).get(user_id_attribute)
        # TODO: 根據項目實際情況進行修改
        user = userid_table.get(user_id)
        return user

    # 拓展 payload
    async def extend_payload(self, payload, *args, **kwargs):
        # 可以獲取 User 中的一些屬性添加到 payload 中
        # 注意:payload 資訊是公開的,這裡不要添加敏感資訊
        user_id_attribute = self.config.user_id()
        user_id = payload.get(user_id_attribute)
        # TODO: 根據項目實際情況進行修改
        user: User = userid_table.get(user_id)
        payload.update({'sex': user.sex})  # 比如添加性別屬性
        return payload

    async def extract_payload(self, req, verify=True, *args, **kwargs):
        return await super().extract_payload(req, verify)


class MyJWTResponse(Responses):

    # 自定義發生異常的返回數據
    @staticmethod
    def exception_response(req: request.Request, exception: exceptions):
        # sanic-jwt.exceptions 下面定義的異常類型:
        # AuthenticationFailed
        # MissingAuthorizationHeader
        # MissingAuthorizationCookie
        # InvalidAuthorizationHeader
        # MissingRegisteredClaim
        # Unauthorized
        msg = str(exception)
        if exception.status_code == 500:
            msg = str(exception)
        elif isinstance(exception, exceptions.AuthenticationFailed):
            msg = str(exception)
        else:
            if "expired" in msg:
                msg = "授權已失效,請重新登錄!"
            else:
                msg = "未授權,請先登錄!"
        result = {
            "status": exception.status_code,
            "data": None,
            "msg": msg
        }
        return response.json(result, status=exception.status_code)


app = Sanic("my_auth_app")
initialize(app, authenticate=authenticate,
           authentication_class=MyJWTAuthentication, configuration_class=MyJWTConfig, responses_class=MyJWTResponse)


@app.route("/index")
@protected()  # 保護該路由,只有授權用戶才能訪問
async def protected_route_index(req: request.Request):
    # 從 request 中獲取 payload,然後返回給前端
    payload = await req.app.auth.extract_payload(req)
    return response.json({'payloadInfo': payload})


@app.route("/info")
@inject_user()  # 注入用戶資訊
@protected()    # 保護該路由,只有授權用戶才能訪問
async def protected_route_info(req: request.Request, user: User):
    if user.black_level == 0:
        return response.json({'userName': user.username, "personalInfo": user.personal_info})
    else:  # 進入黑名單等級之後限制查看
        return response.json({'userName': user.username, "personalInfo": ""})


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080, auto_reload=True)

 

Configuration 參數

 下面列出的參數可以根據需要在前面的 MyJWTConfig 這個類下進行添加設置。

參數 描述 默認 備註
access_token_name
標識訪問令牌的key ‘access_token’
algorithm
生成標記的哈希演算法 ‘HS256’
可選項:
HS256, HS384, HS512,
ES256, ES384, ES512, RS256,
RS384, RS512, PS256, PS384, PS512
auth_mode
是否啟用 /auth 介面 True
authorization_header
HTTP請求 header 中令牌key ‘authorization’
authorization_header_prefix
HTTP請求header中JWT的前綴 ‘Bearer’
authorization_header_refresh_prefix
保留字,未使用 ‘Refresh’
claim_aud
面向的用戶 None
claim_iat
是否啟用生成令牌簽發時間 False
claim_iss
令牌簽發者 None
claim_nbf
是否 啟用生成令牌在簽發後多久生效 功能 False
claim_nbf_delta
令牌在簽發後多久生效 60 * 3,即:3 分鐘
cookie_access_token_name
使用cookie令牌時,cookie中令牌的名稱 ‘access_token’
cookie_domain
cookie所在的域
cookie_httponly
是否啟用 http only cookie True
cookie_refresh_token_name
使用cookie令牌,cookie中刷新令牌的名稱 ‘refresh_token’
cookie_set
啟用cookie令牌 False
cookie_strict
啟用cookie令牌,cookie獲取失敗後是否禁用頭部令牌 False
cookie_token_name
cookie_access_token_name 的別名,用於測試 False 實測該值無效,應使用 cookie_access_token_name
do_protection
啟用@protected裝飾器正常工作 True
expiration_delta
令牌有效期 60 * 5 * 6(30分鐘)
generate_refresh_token
創建和返回刷新令牌的方法 sanic_jwt.utils.generate_refresh_token
leeway
系統時間配置中微小更改的迴旋時間秒 60 * 3(3分鐘)
path_to_authenticate
身份驗證介面路徑 ‘/’
path_to_refresh
刷新令牌介面路徑 ‘/refresh’
path_to_retrieve_user
當前用戶介面路徑 ‘/me’
path_to_verify
令牌驗證介面 ‘/verify’
private_key
用於生成令牌的私鑰,依賴於使用的散列演算法 None
public_key
secret的別名
query_string_access_token_name
查詢字元串令牌,cookie中令牌的名稱 ‘access_token’
query_string_refresh_token_name
查詢字元串令牌,cookie中刷新令牌的名稱 ‘refresh_token’
query_string_set
開啟查詢字元串令牌 False
query_string_strict
開啟查詢字元串令牌,查詢字元串不存在是否禁用頭部令牌 False
refresh_token_enabled
啟用刷新令牌 False 如果開啟,就需要存儲refresh_token,
所以需要額外程式碼實現。
refresh_token_name
刷新令牌的key ‘refresh_token’
scopes_enabled
啟用scope塊並將作用域添加到jwt payload False
scopes_name
jwt payload中scope的key ‘scopes’
secret
用於哈希演算法生成和簽名JWT,每個應用應該設置自已的值 ‘This is a big secret. Shhhhh’
strict_slashes
啟用對介面url執行嚴格的/匹配 False
url_prefix
sanic jwt默認介面的前綴 ‘/auth’
user_id
包含用戶 id 的用戶對象的鍵或屬性 ‘user_id’
verify_exp 開啟令牌過期驗證 True

 

 

參考:

sanic-jwt

//blog.csdn.net/zhouping118/article/details/88736986