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.*)$', 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
# 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__()