python --django(初始环境) token问题

utilts中

import base64
import datetime
import json
import threading

import jwt
from Crypto.Cipher import AES
from django.db import DatabaseError
from jwt import InvalidTokenError
from loguru import logger
from redis import RedisError
from rest_framework import status, mixins
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.response import Response
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.viewsets import GenericViewSet

from coal_trading.settings import SECRET_KEY


class BaseAuthException(Exception):
    '''自定义权限异常'''

    def __init__(self, msg):
        self.msg = msg
        super().__init__(self, msg)

    def __str__(self):
        return str(self.msg)

    def __call__(self, *args, **kwargs):
        return str(self.msg)


def exception_handler(exc, context):
    """
    自定义异常处理  需要settings中指定异常方法
    :param exc: 别的地方抛的异常就会传给exc
    :param context: 字典形式。抛出异常的上下文(即抛出异常的出处;即抛出异常的视图)
    :return: Response响应对象
    """
    # 调用drf框架原生的异常处理方法,把异常和异常出处交给他处理,如果是序列化器异常就直接处理,处理之后就直接返回
    response = drf_exception_handler(exc, context)
    logger.info(f'响应-->{response}异常-->{exc}出处-->{context}')
    # 如果响应为空表示不是序列化器异常,补充数据库异常 和 认证异常
    if response is None:
        if isinstance(exc, DatabaseError) or isinstance(exc, RedisError):
            # 数据库异常
            response = Response({'msg': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)

        elif isinstance(exc, BaseAuthException):
            response = Response({'code': 500, 'msg': '无效的令牌或令牌已过期,请重新登录'})

        # --- 未捕获则直接报错

    return response


class LoginRequiredAuthentication(BaseAuthentication):
    """登录认证"""

    # 如果用户身份验证成功需要返回一个二元组(user, token)
    def authenticate(self, request):
        token = request.META.get('HTTP_AUTHORIZATION')
        if token:
            try:
                payload = jwt.decode(token, SECRET_KEY)
                user = BackstageUser()
                logger.info(f'解析参数-->{payload}')
                user.backstage_user_id = payload['userid']
                user.is_authenticated = True
                return user, token
            except InvalidTokenError:
                raise BaseAuthException('无效的令牌或令牌已过期,请重新登录')
        raise AuthenticationFailed('请提供用户身份令牌')


class SingletonType(type):
    '''多线程(元类单例)'''
    _instance_lock = threading.Lock()

    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            with SingletonType._instance_lock:
                if not hasattr(cls, "_instance"):
                    cls._instance = super(SingletonType, cls).__call__(*args, **kwargs)
        return cls._instance


def singleton(cls, *args, **kwargs) -> object:
    '''单例装饰器'''
    instances = {}

    def _singleton():
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return _singleton


class AsyncTask(object):
    ''' 异步执行任务(基于延迟任务) '''

    @staticmethod
    def start(func, *args, timer=0) -> None:
        '''
        :param func: 函数
        :param timer: 延迟时间
        :param args: 位置参数
        :Eg
        def a(b, c):
            print(b, c)
        wa = AsyncTask()
        wa.start(a, 88, 100)
        '''
        t = threading.Timer(timer, func, (*args,))
        logger.info('异步任务触发,开始执行')
        t.start()

    def __call__(self, func, *args, timer=0): AsyncTask.start(func, *args, timer=0)

    def __str__(self): return self.__class__.__name__

    def __repr__(self): return self.__class__.__name__


class AuthUser(object):
    '''权限验证'''

    @staticmethod
    def create_token(user_id) -> str:
        '''生成token'''
        payload = {
            'exp': datetime.datetime.utcnow() + datetime.timedelta(days=99999),
            'userid': user_id,
        }
        token = jwt.encode(payload, SECRET_KEY).decode()
        return token

    @staticmethod
    def auth_token(token) -> tuple:
        '''解token'''
        try:
            user_id = jwt.decode(token, SECRET_KEY).get('userid')
            return True, user_id
        except InvalidTokenError as ext:
            logger.info(ext)
        return False, False


class BaseUpdateModel(mixins.UpdateModelMixin):
    '''重构update方法'''

    def update(self, request, *args, **kwargs) -> Response:
        super().update(request, *args, **kwargs)
        return Response({
            'code': 0,
            'msg': '修改成功'
        })


class BaseDeleteModel(mixins.DestroyModelMixin):
    '''重构delete方法'''

    def destroy(self, request, *args, **kwargs) -> Response:
        super().destroy(request, *args, **kwargs)
        return Response({
            'code': 0,
            'msg': '删除成功'
        })


class BaseRetrieveModel(mixins.RetrieveModelMixin):
    '''重构主键查询方法get'''

    def retrieve(self, request, *args, **kwargs) -> Response:
        return Response({
            'code': 0,
            'msg': '查询成功',
            'results': super().retrieve(request, *args, **kwargs).data
        })


class BaseCreateModel(mixins.CreateModelMixin):
    '''重构create查询方法post'''

    def create(self, request, *args, **kwargs) -> Response:
        super().create(request, *args, **kwargs)
        return Response({
            'code': 0,
            'msg': '新增成功'
        })


class BaseListModelMixin(mixins.ListModelMixin):
    '''重构list方法,当未配置分页时数据必须小于等于1'''
    def list(self, request, *args, **kwargs) -> Response:
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        assert queryset.count() <= 1
        serializer = self.get_serializer(queryset.first())
        return Response({
            'code': 0,
            'msg': '查询成功',
            'results': serializer.data
        })


class BaseReadOnlyModelViewSet(BaseListModelMixin,
                               BaseRetrieveModel,
                               GenericViewSet
                               ): ...  # 重构 ReadOnlyModelViewSet


class BaseModelViewSet(BaseCreateModel,
                       BaseRetrieveModel,
                       BaseUpdateModel,
                       BaseDeleteModel,
                       BaseListModelMixin,
                       GenericViewSet
                       ): ...  # 重构ModelViewSet


class WXBizDataCrypt(object):
    # 解密用户信息
    def __init__(self, appid, session_key):
        self.appId = appid
        self.sessionKey = session_key

    def decrypt(self, encryptedData, iv):
        # base64 decode
        session_key = base64.b64decode(self.sessionKey)
        encryptedData = base64.b64decode(encryptedData)
        iv = base64.b64decode(iv)
        cipher = AES.new(session_key, AES.MODE_CBC, iv)

        decrypted = json.loads(self._unpad(cipher.decrypt(encryptedData)).decode())

        if decrypted['watermark']['appid'] != self.appId:
            raise Exception('Invalid Buffer')

        return decrypted

    def _unpad(self, s):
        return s[:-ord(s[len(s) - 1:])]

helps中

import random
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from collections import OrderedDict


class MyPageNumberPagination(PageNumberPagination):
    '''自定义分页'''
    page_size = 10
    page_size_query_param = 'size'
    max_page_size = 100

    def get_paginated_response(self, data):
        return Response(OrderedDict([
            ('code', 0),
            ('msg', '查询成功'),
            ('count', self.page.paginator.count),
            ('next', self.get_next_link()),
            ('previous', self.get_previous_link()),
            ('results', data)
        ]))

db_routers中

import random

import loguru


class MasterSlaveRouter(object):
    """数据库主从读写分离路由"""

    @staticmethod  # 读
    def db_for_read(model, **hints):
        # loguru.logger.info('db读写分离-->读')
        return 'default'  # random.choice(('slave1', 'slave2', 'slave3'))  # 如果大概率要使用slave1,那就在写一个

    @staticmethod  # 写
    def db_for_write(model, **hints):
        loguru.logger.info('db读写分离-->写')
        return 'default'

    @staticmethod
    def allow_relation(obj1, obj2, **hints):
        '''是否运行关联操作'''
        # loguru.logger.info('db读写分离-->运行关联操作')
        return None

    @staticmethod  # 是否允许数据迁移(正反向工程)
    def allow_migrate(db, app_label, model_name=None, **hints):
        # loguru.logger.info('db读写分离-->许数据迁移(正反向工程)')
        return True

middleware中

from django.utils.deprecation import MiddlewareMixin
from requests import Response
import requests
import threading
from loguru import logger


class GETAddressMiddleWare(MiddlewareMixin):
    '''获取用户IP的中间件'''

    def process_request(self, request):
        '''执行请求前的中间件'''
        ip = request.META.get("HTTP_X_FORWARDED_FOR", "")
        if not ip:
            ip = request.META.get('REMOTE_ADDR', "")
        client_ip = ip.split(",")[-1].strip() if ip else ""
        logger.warning('当前请求IP为-->{}'.format(client_ip))
        # t = threading.Timer(0, get_ip, (str(client_ip), ))
        # t.start()

    def process_response(self, request, response):
        # 基于请求响应
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        '''
        #在视图之前执行 顺序执行
        #return view_func(request)
        '''

    def process_exception(self, request, exception):
        '''自定义异常处理器'''
        print(exception)
        # return HttpResponse(exception) #返回错误信息


def get_ip(ip: str):
    key = 'a90f24c2491eb***8'
    url = f'http://apis.juhe.cn/ip/ipNewV3?ip={ip}&key={key}'
    res = requests.get(url)
    try:
        dict_res: dict = res.json().get('result')
        print(f'国家  -->{dict_res.get("Country")}')
        print(f'省份  -->{dict_res.get("Province")}')
        print(f'市区  -->{dict_res.get("City")}')
        print(f'运营商-->{dict_res.get("Isp")}')
        print('=' * 20)
    except Exception as ext:
        print(f'{ext}')

主项目urls中

from django.urls import path, include, re_path
from django.views.static import serve
from *** import settings
from rest_framework_swagger.views import get_swagger_view

schema_view = get_swagger_view(title='***--api文档')

urlpatterns = [
    path('backstage/api/', include('backstage.urls')),
    path('front_end/api/', include('front_end.urls')),
    re_path(r'media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
    path('api/docs/', schema_view),
]

settings中

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'corsheaders',
    'front_end',
    'backstage',
    'django_filters',
    'rest_framework',
    'rest_framework_swagger'
]


MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'backstage.middleware.GETAddressMiddleWare',
]

DATABASES = {
    'default': {
        'ENGINE': 'dj_db_conn_pool.backends.mysql',
        # 'django.db.backends.mysql',  pip install django-db-connection-pool
        'NAME': 'polls',
        'HOST': 'IP',
        'PORT': 3306,
        'USER': 'root',
        'PASSWORD': 'Admin123.',
        'CHARSET': 'utf8',
        'TIME_ZONE': 'Asia/Shanghai',
        'OPTIONS': {'charset': 'utf8mb4'},
        'POOL_OPTIONS': {
            'POOL_SIZE': 10,
            'MAX_OVERFLOW': 10
        }
    }
}

REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
# 配置默认页面大小
'PAGE_SIZE': 10,
# 配置默认的分页类
'DEFAULT_PAGINATION_CLASS': 'backstage.helps.MyPageNumberPagination',

'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S',  # 时间相关的字段

# 配置异常处理器
# 'EXCEPTION_HANDLER': 'api.exceptions.exception_handler',
'EXCEPTION_HANDLER': 'backstage.utils.exception_handler',

# 配置默认解析器
# 'DEFAULT_PARSER_CLASSES': (
# 'rest_framework.parsers.JSONParser',
# 'rest_framework.parsers.FormParser',
# 'rest_framework.parsers.MultiPartParser',
# ),

# 配置默认限流类
# 'DEFAULT_THROTTLE_CLASSES': (),

# 配置默认授权类
# 'DEFAULT_PERMISSION_CLASSES': (
# 'rest_framework.permissions.IsAuthenticated',
# ),

# 关闭api调试界面
'DEFAULT_RENDERER_CLASSES': (
    'rest_framework.renderers.JSONRenderer',  # json渲染
    'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览器渲染(生产环境可关掉)
)

}

# # 配置允许跨域访问接口数据
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = ()
# # 跨域访问允许的请求头
CORS_ALLOW_HEADERS = (
    'accept',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
    'token',
)

# # 跨域访问支持的HTTP请求方法
CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
)

# # 避免浏览器自作聪明推断内容类型(避免跨站脚本攻击风险)
SECURE_CONTENT_TYPE_NOSNIFF = True

#  日志配置(日志级别越低内容越详细)
# LOGGING = {
#     'version': 1,
#     'disable_existing_loggers': False,
#     'formatters': {
#         'simple': {
#             'format': '%(asctime)s %(module)s.%(funcName)s: %(message)s',
#             'datefmt': '%Y-%m-%d %H:%M:%S',
#         },
#         'verbose': {
#             'format': '%(asctime)s %(levelname)s [%(process)d-%(threadName)s] '
#                       '%(module)s.%(funcName)s line %(lineno)d: %(message)s',
#             'datefmt': '%Y-%m-%d %H:%M:%S',
#         }
#     },
#     'filters': {
#         'require_debug_true': {
#             '()': 'django.utils.log.RequireDebugTrue',
#         },
#     },
#     'handlers': {
#         'console': {
#             'class': 'logging.StreamHandler',
#             'filters': ['require_debug_true'],
#             'formatter': 'simple',
#             'level': 'DEBUG',
#         },
#         'file1': {
#             'class': 'logging.handlers.TimedRotatingFileHandler',
#             'filename': 'access.logs',
#             'when': 'W0',
#             'backupCount': 12,
#             'formatter': 'simple',
#             'level': 'DEBUG',
#         },
#         'file2': {
#             'class': 'logging.handlers.TimedRotatingFileHandler',
#             'filename': 'error.logs',
#             'when': 'D',
#             'backupCount': 31,
#             'formatter': 'verbose',
#             'level': 'WARNING',
#         },
#     },
#     'loggers': {
#         'django.db': {
#             'handlers': ['console', 'file1', 'file2'],
#             'propagate': True,
#             'level': 'DEBUG',
#         },
#     }
# }

# 保持HTTPS连接的时间
# SECURE_HSTS_SECONDS = 3600
# SECURE_HSTS_INCLUDE_SUBDOMAINS = True
# SECURE_HSTS_PRELOAD = True

# 自动重定向到安全连接
# SECURE_SSL_REDIRECT = True

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Chongqing'

USE_I18N = True

USE_L10N = True

USE_TZ = True

CACHES = {
    # 默认缓存
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': [
            'redis://IP:54321/0',
        ],
        'KEY_PREFIX': 'draw:search',  # 给键加上固定的值
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
            'CONNECTION_POOL_KWARGS': {
                'max_connections': 1000,
            },
            'PASSWORD': '密码',
        }
    }
}

MEDIA_URL = "/media/"  # 设置获取文件时的访问根路径
MEDIA_ROOT = os.path.join(BASE_DIR, "media")

STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'), ]
STATIC_URL = '/static/'
# STATIC_ROOT = '/root/project/static'   #克隆静态

gun_conf

import multiprocessing

bind = '127.0.0.1:8001'
workers = 1 # multiprocessing.cpu_count() * 2 + 1

backlog = 512 # 服务器中在pending状态的最大连接数,即client处于waiting的数目。超过这个数目, client连接会得到一个error 建议取值64-2048

worker_class = 'gevent'

timeout = 30

limit_request_line = 0  # http request line最大字节数。值范围0-8190, 0表示无限制。
limit_request_field = 32768  # http request中 header字段数的最大值。缺省为100,最大32768。
limit_request_field_size = 0  # http request header字段最大字节数。0表示无限制。


reload = True

Token问题(支持正在使用的问题无限延长token有效期)

# coding:utf-8
import base64
import time
from hashlib import md5
from string import ascii_lowercase
import redis
import random


class CreateToken(object):
    '''token操作'''
    client = redis.Redis(host='ip', port=54321, db=15, password='Admin123.')

    @classmethod
    def _random_str(cls):
        '''根据随机字符和时间戳生成一个字符串'''
        return f'{random.sample(ascii_lowercase, k=1)[0]}{int(time.time())}'.encode('utf-8')

    @classmethod
    def _md5(cls):
        '''对字符传进行md5'''
        return md5(cls._random_str()).hexdigest()

    @classmethod
    def _b64(cls, user_id):
        '''对user_id进行base64加密'''
        return base64.b64encode(cls._des_user(user_id).encode('utf-8')).decode()

    @classmethod
    def _des_user(cls, user_id):
        '''对user_id进行加盐操作'''
        return '{},{}'.format(user_id, str(time.time()))

    @classmethod
    def create_token(cls, user_id):
        '''根据user_id生成token'''
        return '{}.{}.{}'.format(cls._md5(), cls._md5(), cls._b64(user_id))

    @classmethod
    def analysis_token(cls, token):
        '''根据token解user_id'''
        return base64.b64decode(token.split('.')[2]).decode('utf-8').split(',')[0]

    @classmethod
    def add(cls, key, value='token', timeout=7200):
        '''对redis添加token'''
        cls.client.set(key, value)
        cls.ex(key, ex=timeout)

    @classmethod
    def ex(cls, key, ex=0):
        '''对redis的指定token添加存活时间'''
        cls.client.expire(key, ex)

    @classmethod
    def ttl(cls, key):
        '''根据key拿存活时间'''
        return cls.client.ttl(key)

    @classmethod
    def auth_user(cls, token):
        '''从redis根据token拿数据'''
        return cls.client.get(token)

    def __setattr__(self, key, value):
        self.__dict__[key] = value

    def __repr__(self):
        return self.__str__()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

像风一样的男人@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值