[ Flask自定义jwt_token ]


import jwt
import datetime
from flask import request, g
from .http import fail_response
from flask import has_request_context
from functools import wraps
from ...models import User
import re
from typing import Union


JWT_CONFIG: dict[str, Union[str, datetime.timedelta]] = {
    'ACCESS_TIMEOUT': datetime.timedelta(minutes=15),
    'REFRESH_TIMEOUT': datetime.timedelta(minutes=60),
    'KEY': 'ABC',                   # 加密钥匙
    'ALGORITHM': 'HS256',           # 加密类型
    'USER_ID_FILED': 'id',          # 查询的用户id名
    'AUTH_HEADER_TYPES': 'Bearer'   # 接收权限标头1
}


class AnonymousUserMixin:
    """
    This is the default object for representing an anonymous user.
    """

    @property
    def is_authenticated(self):
        return False

    @property
    def is_active(self):
        return False

    @property
    def is_anonymous(self):
        return True

    def get_id(self):
        return

# 获取用户


def get_user():
    if has_request_context():
        if "jwt_user" not in g:
            # current_app.login_manager._load_user()
            return AnonymousUserMixin()  # 匿名用户
        return g.jwt_user
    return None

# 通过用户名和密码获取用户


def check_user(username, password):
    user = User.query.filter(User.username == username).first()
    if not user:
        return fail_response({'msg': '用户名错误', 'username': username})

    if not user.validate_password(password):
        return fail_response({'msg': '密码错误', 'password': password})
    return user


# 通过id获取用户
def jwt_filter_user(id: int):
    user = User.query.filter(User.id == id).first()
    if not user:
        return fail_response({'msg': '不存在的用户id', 'pk': id})
    return user

# 装饰器: 获取请求头的 Bearer


def headersauth(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        if token := re.match(r'{0}\s(?P<token>.*)'.format(JWT_CONFIG.get('AUTH_HEADER_TYPES', 'Bearer')), str(request.headers.get('Authorization'))):
            token = flask_jwt.access(access=token.group('token'))
            id = token.get(JWT_CONFIG.get('USER_ID_FILED'))
            g.jwt_user = jwt_filter_user(int(id))
        else:
            return fail_response({'msg': '没有提供认证'})
        return func(*args, **kwargs)
    return wrapper


class MyTokenJwt:
    def __init__(self, access_timeout, refresh_timeout, algorithm, key) -> None:
        # self.now = datetime.datetime.now(tz=datetime.timezone.utc) # 当前时间
        self.access_timeout = access_timeout or datetime.timedelta(
            minutes=15)  # access令牌超时时间
        self.refresh_timeout = refresh_timeout or datetime.timedelta(
            minutes=60)  # refresh令牌超时时间
        self.key = key  # 加盐值
        self.algorithm = algorithm  # 使用的加密算法

    # 签证(access, refresh)
    def visa(self, **kwargs):
        return {'access': self.jwt_encode(self.access_timeout, token_type='access', **kwargs), 'refresh': self.jwt_encode(self.refresh_timeout, token_type='refresh', **kwargs)}

    # 用于access令牌认证
    def access(self, token):
        return self.jwt_decode(token, token_type='access')

    # 用于refresh刷新令牌认证
    def refresh(self, token):
        token = self.jwt_decode(token, token_type='refresh')
        return {'token': self.jwt_encode(**token)}

    # 用于jwt加密, 返回令牌
    def jwt_encode(self, token_timeout=JWT_CONFIG['REFRESH_TIMEOUT'], **kwargs):
        now = datetime.datetime.now(tz=datetime.timezone.utc)
        payload = {**kwargs}
        payload['exp'] = now + token_timeout
        payload['iat'] = now
        payload.pop('password', None)
        return jwt.encode(payload=payload, key=self.key, algorithm=self.algorithm)

    # 用户jwt解密, 返回字典
    def jwt_decode(self, token: str, token_type=None):
        try:
            token_dict = jwt.decode(jwt=token, key=self.key,
                                    algorithms=[self.algorithm])
            if token_type and token_type != token_dict.get('token_type'):
                return fail_response({'error_token': '令牌类型错误'})
        except jwt.ExpiredSignatureError:
            return fail_response({'error_token': '签证已经过期'})
        except jwt.exceptions.DecodeError:
            return fail_response({'error_token': 'token是必须的'})
        return token_dict


flask_jwt = MyTokenJwt(access_timeout=JWT_CONFIG.get('ACCESS_TIMEOUT'), refresh_timeout=JWT_CONFIG.get(
    'REFRESH_TIMEOUT'), key=JWT_CONFIG.get('KEY'), algorithm=JWT_CONFIG.get('ALGORITHM'))

# print(my_token.visa(username='陈海峰'))
# print(my_token.access('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2ODA2MjI0NDIsImlhdCI6MTY4MDYxODg0MiwidG9rZW5fdHlwZSI6InJlZnJlc2giLCJ1c2VybmFtZSI6Ilx1OTY0OFx1NmQ3N1x1NWNmMCJ9.Fi23bQHpAfZRm7Efd-Q7PHtBH7gUsYwHkkPoTtOJap0'))