Compare commits

...

10 Commits

Author SHA1 Message Date
jayhgq
f1ec97436d 1.更新数据库结构,添加菜单表、角色表、角色菜单表、角色用户表 2024-09-17 17:14:15 +08:00
jayhgq
4e8c817880 1.添加备注信息
2.优化代理逻辑
2024-09-17 00:42:28 +08:00
jayhgq
4d5846f81e 1.增加config配置文件,自定义是否是用加密
2.增加区分加密数据库和非加密数据库
3.重构现有接口,区分是否加密和是用的加密方式,目前支持caesar和base64两种加密方式,仅在数据库存储时加密
2024-09-17 00:00:45 +08:00
jayhgq
7bf39c83bd 1.优化Base64算法,增加异常捕获
2.调整auth所有接口为base64加密存储
3.修复无法获取token的问题
2024-09-16 00:08:13 +08:00
jayhgq
32e82441c2 1.api中添加Base64算法
2.apps中添加home,用于测试算法,后面将首页写到此处
2024-09-09 23:31:33 +08:00
jayhgq
b28732d3ef 1.调整路由命名
2.api中添加Caesar算法
2024-09-07 00:03:37 +08:00
jayhgq
adf533c594 修改api-getconfig视图的参数名称 2024-09-06 23:24:12 +08:00
jayhgq
3520699304 添加必要的异常捕获 2024-08-21 22:44:50 +08:00
jayhgq
d01b0106cb 添加用户登录视图login_user 2024-08-21 00:07:34 +08:00
jayhgq
adb2664383 更新数据库系统配置表 2024-08-18 22:12:39 +08:00
21 changed files with 416 additions and 36 deletions

View File

@ -37,6 +37,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'apps.auth.apps.AuthConfig', 'apps.auth.apps.AuthConfig',
'apps.api.apps.ApiConfig', 'apps.api.apps.ApiConfig',
'apps.home.apps.HomeConfig',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -77,6 +78,10 @@ DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'data/db.sqlite3', 'NAME': BASE_DIR / 'data/db.sqlite3',
},
'cypher': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'data/db_cypher.sqlite3',
} }
} }

View File

@ -18,11 +18,13 @@ from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from apps.home import views as home_views
urlpatterns = [ urlpatterns = [
# path('admin/', admin.site.urls), # path('admin/', admin.site.urls),
# path('login', include('apps.login.urls')) # path('login', include('apps.login.urls')),
# path('auth', include('apps.auth.urls')) # path('auth', include('apps.auth.urls')),
path('home/', home_views.home, name='home'),
path('api/', include('apps.api.urls')), path('api/', include('apps.api.urls')),
path('auth/', include('apps.auth.urls')) path('auth/', include('apps.auth.urls')),
] + static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT) ] + static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)

125
apps/api/common.py Normal file
View File

@ -0,0 +1,125 @@
import re
class CaesarCypherClass:
"""
恺撒密码提供以恺撒密码方法进行加密及解密的方法加密方法使用CaesarEncode()函数解密方法使用CaesarDecode()函数
"""
def __init__(self, *args, **kwargs):
pass
@staticmethod
def caesar_encode(s):
"""
恺撒密码加密方法需要提供需要加密的明文
"""
s_encode = ''
for c in s:
if 'a' <= c <= 'z':
s_encode += chr(ord('a') + (ord(c) - ord('a') + 3) % 26)
elif 'A' <= c <= 'Z':
s_encode += chr(ord('A') + (ord(c) - ord('A') + 3) % 26)
elif 0x4E00 <= ord(c) <= 0x9FA5:
s_encode += chr(ord(c) + 3)
elif '0' <= c <= '9':
s_encode += chr(ord('0') + (ord(c) - ord('0') + 3) % 10)
else:
s_encode += c
return s_encode
@staticmethod
def caesar_decode(s):
"""
恺撒密码解密方法需要提供需要解密的密文
"""
s_decode = ''
for c in s:
if 'a' <= c <= 'z':
s_decode += chr(ord('a') + (ord(c) - ord('a') - 3) % 26)
elif 'A' <= c <= 'Z':
s_decode += chr(ord('A') + (ord(c) - ord('A') - 3) % 26)
elif 0x4E00 <= ord(c) <= 0x9FA5:
s_decode += chr(ord(c) - 3)
elif '0' <= c <= '9':
s_decode += chr(ord('0') + (ord(c) - ord('0') - 3) % 10)
else:
s_decode += c
return s_decode
class Base64CypherClass:
"""
Base64的加解密算法最简单的加密方式可加密短的文字小图片小文件图片文件大小不宜超过10M
"""
def __init__(self, *args, **kwargs):
"""
Base64类初始化函数
:param args:
:param kwargs:
"""
import importlib
self.base64 = importlib.import_module('base64')
self.os = importlib.import_module('os')
self.time = importlib.import_module('time')
self.re = importlib.import_module('re')
@staticmethod
def base64_encode_str(self, s):
"""
Base64字符串加密
:param self:
:param s: 要加密的字符串
:return: 加密后的字符串
"""
return self.base64.b64encode(s.encode('utf-8'))
@staticmethod
def base64_decode_str(self, s):
"""
Base64字符串解密解密前先判断是否为Base64加密方式
:param self:
:param s: 要解密的字符串
:return: 解密后的字符串
"""
try:
self.base64.b64decode(s)
return self.base64.b64decode(s).decode('utf-8')
except Exception as e:
return f"base64解密失败请确定加密方式是否正确。错误信息{e}"
@staticmethod
def base64_encode_pic(self, pic):
"""
Base64加密图片路径不存在则返回"图片路径不存在"
:param self:
:param pic: 要加密的图片路径
:return: 返回加密的base64字符
"""
if self.os.path.exists(pic):
with open(pic, 'rb') as f:
read_pic = open(pic, 'rb')
read_data = read_pic.read()
read_pic.close()
return self.base64.b64encode(read_data)
else:
return "图片路径不存在"
@staticmethod
def base64_decode_pic(self, pic_bs64):
"""
Base64解密图片
:param self:
:param pic_bs64:
:return: 返回图片路径
"""
pic_path = f"upload/temp/pic{int(self.time.time())}"
if self.os.path.exists(f"{pic_path}.jpg"):
self.os.remove(f"{pic_path}.jpg")
elif not self.os.path.exists("upload/temp/pic"):
self.os.path.mkdir("upload/temp/pic")
with open(f"{pic_path}.jpg", 'wb') as f:
f.write(self.base64.b64decode(pic_bs64))
return f"{pic_path}.jpg"

8
apps/api/config.py Normal file
View File

@ -0,0 +1,8 @@
class Config:
config = {
"isCypher": False,
"CypherMethod": "base64"
}
def getconfig(self, config_name):
return self.config[config_name]

View File

@ -2,5 +2,5 @@ from django.urls import path
from apps.api import views from apps.api import views
urlpatterns = [ urlpatterns = [
path("getconfig/", views.getconfig, name="getconfig"), path("getconfig/", views.get_config, name="getconfig"),
] ]

View File

@ -1,15 +1,44 @@
from django.shortcuts import HttpResponse from django.shortcuts import HttpResponse
from apps.api import models as m_api from apps.api import models as m_api
from django.views.decorators.http import require_http_methods, require_POST, require_GET from django.views.decorators.http import require_http_methods, require_POST, require_GET
from apps.api.common import CaesarCypherClass, Base64CypherClass
from apps.api.config import Config
config = Config()
caesar = CaesarCypherClass()
base64 = Base64CypherClass()
# Create your views here. # Create your views here.
@require_POST @require_POST
def getconfig(request): def get_config(request):
"""
获取系统配置的接口通过identity标识字段查询param参数并返回如果使用加密版数据库则根据加密方式进行解密后返回
:param request: identity标识字段
:return: 获取到的参数param
"""
try: try:
param = request.POST.get("param") identity = request.POST.get("param")
title = m_api.SysConfig.objects.filter(identity=param).first().param if config.getconfig("isCypher"): # 启用加密数据库
return HttpResponse(title) param_base64 = m_api.SysConfig.objects.using("cypher").filter(identity=identity).first().param
if config.getconfig("CypherMethod") == "caesar": # 加密方式为Caesar
param = caesar.caesar_decode(param_base64)
return HttpResponse(param)
else: # 加密方式为Base64
param = base64.base64_decode_str(base64, param_base64)
return HttpResponse(param)
else: # 不加密的数据库
param = m_api.SysConfig.objects.using("default").filter(identity=identity).first().param
return HttpResponse(param)
except Exception as e: except Exception as e:
print(f"报错了:{e}") print(f"报错了:{e}")
return HttpResponse("报错了") return HttpResponse(f"报错了:{e}")
@require_POST
def add_config(request):
try:
pass
except Exception as e:
print(f"报错了:{e}")
return HttpResponse(f"报错了:{e}")

View File

@ -0,0 +1,52 @@
# Generated by Django 5.1 on 2024-09-17 09:09
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Menu',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, unique=True, verbose_name='id')),
('menu_name', models.CharField(max_length=50, verbose_name='菜单名称')),
('parent_id', models.IntegerField(max_length=50, verbose_name='父菜单')),
('path', models.CharField(max_length=128, verbose_name='路由地址')),
('order', models.IntegerField(default=0, max_length=5, verbose_name='排序')),
('create_time', models.DateTimeField(verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='最后更新时间')),
],
),
migrations.CreateModel(
name='Role',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, unique=True, verbose_name='id')),
('role_name', models.CharField(max_length=50, verbose_name='角色名称')),
('role_name_en', models.CharField(max_length=50, verbose_name='角色英文名称')),
('create_time', models.DateTimeField(verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='最后更新时间')),
],
),
migrations.CreateModel(
name='RoleMenu',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, unique=True, verbose_name='id')),
('menu_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.menu', verbose_name='菜单ID')),
('role_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.role', verbose_name='角色ID')),
],
),
migrations.CreateModel(
name='RoleUser',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, unique=True, verbose_name='id')),
('role_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.role', verbose_name='角色ID')),
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.user', verbose_name='用户ID')),
],
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 5.1 on 2024-09-17 09:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0002_menu_role_rolemenu_roleuser'),
]
operations = [
migrations.AlterField(
model_name='menu',
name='order',
field=models.IntegerField(default=0, verbose_name='排序'),
),
migrations.AlterField(
model_name='menu',
name='parent_id',
field=models.IntegerField(verbose_name='父菜单'),
),
]

View File

@ -35,3 +35,33 @@ class User(models.Model):
return self.avatar.url return self.avatar.url
else: else:
return '/media/avatar/default.jpg' return '/media/avatar/default.jpg'
class Menu(models.Model):
id = models.AutoField(verbose_name="id", primary_key=True, unique=True)
menu_name = models.CharField(verbose_name="菜单名称", max_length=50, null=False, blank=False)
parent_id = models.IntegerField(verbose_name="父菜单")
path = models.CharField(verbose_name="路由地址", max_length=128)
order = models.IntegerField(verbose_name="排序", default=0)
create_time = models.DateTimeField(verbose_name="创建时间")
update_time = models.DateTimeField(verbose_name="最后更新时间", auto_now=True)
class Role(models.Model):
id = models.AutoField(verbose_name="id", primary_key=True, unique=True)
role_name = models.CharField(verbose_name="角色名称", max_length=50, null=False, blank=False)
role_name_en = models.CharField(verbose_name="角色英文名称", max_length=50)
create_time = models.DateTimeField(verbose_name="创建时间")
update_time = models.DateTimeField(verbose_name="最后更新时间", auto_now=True)
class RoleMenu(models.Model):
id = models.AutoField(verbose_name="id", primary_key=True, unique=True)
menu_id = models.ForeignKey(verbose_name="菜单ID", to="Menu", to_field="id", on_delete=models.CASCADE)
role_id = models.ForeignKey(verbose_name="角色ID", to="Role", to_field="id", on_delete=models.CASCADE)
class RoleUser(models.Model):
id = models.AutoField(verbose_name="id", primary_key=True, unique=True)
role_id = models.ForeignKey(verbose_name="角色ID", to="Role", to_field="id", on_delete=models.CASCADE)
user_id = models.ForeignKey(verbose_name="用户ID", to="User", to_field="id", on_delete=models.CASCADE)

View File

@ -5,4 +5,5 @@ urlpatterns = [
path("gettoken/", views.gettoken, name="getToken"), path("gettoken/", views.gettoken, name="getToken"),
path("searchuser/", views.search_user, name="searchuser"), path("searchuser/", views.search_user, name="searchuser"),
path("adduser/", views.add_user, name="addUser"), path("adduser/", views.add_user, name="addUser"),
path("login/", views.login_user, name="loginUser"),
] ]

View File

@ -1,12 +1,19 @@
import json, datetime, base64 import binascii
import json, datetime
from django.shortcuts import HttpResponse from django.shortcuts import HttpResponse
from django.middleware.csrf import get_token from django.middleware.csrf import get_token
from django.views.decorators.http import require_GET, require_POST from django.views.decorators.http import require_GET, require_POST
from apps.auth import models as auth_models from apps.auth import models as auth_models
from django.contrib.auth.hashers import make_password, check_password from django.contrib.auth.hashers import make_password, check_password
from apps.api.common import CaesarCypherClass, Base64CypherClass
from apps.api.config import Config
# Create your views here. # Create your views here.
config = Config()
base64 = Base64CypherClass()
caesar = CaesarCypherClass()
@require_GET @require_GET
def gettoken(request): def gettoken(request):
""" """
@ -21,41 +28,104 @@ def gettoken(request):
@require_POST @require_POST
def search_user(request): def search_user(request):
""" """
查询用户名 查询用户名是否存在若存在则返回True不存在则返回False如果使用加密版数据库则根据加密方式进行加密后再查询数据库
:param request: :param request:
:return: :return:
""" """
username = request.POST.get('username') if config.getconfig("isCypher"): # 启用加密数据库
user = auth_models.User.objects.filter(username=username) if config.getconfig("CypherMethod") == "caesar": # 加密方式为Caesar
username = caesar.caesar_encode(request.POST.get("username"))
user = auth_models.User.objects.using("cypher").filter(username=username)
else: # 加密方式为Base64
username = base64.base64_encode_str(base64, request.POST.get("username")).decode('utf-8')
user = auth_models.User.objects.using("cypher").filter(username=username)
else: # 不加密的数据库
username = request.POST.get("username")
user = auth_models.User.objects.using("default").filter(username=username)
if user.exists(): if user.exists():
return HttpResponse("用户名已存在")
else:
return HttpResponse(True) return HttpResponse(True)
else:
return HttpResponse(False)
@require_POST @require_POST
def add_user(request): def add_user(request):
""" """
用户注册 用户注册前端需要将用户名密码以base64的方式加密后传输存储密码时是用md5进行存储
如果使用加密版数据库则根据加密方式将用户名邮箱电话加密后存储到数据库
用户头像目前以路径的方式存储
:param request: POST提交注册信息 :param request: POST提交注册信息
:return: 注册结果 :return: 注册结果
""" """
username = request.POST.get("username") try:
pwd_base64 = base64.b64decode(request.POST.get("pwd")) create_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
pwd = make_password(pwd_base64) last_login_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
email = request.POST.get("email") avatar = request.FILES.get("avatar")
phone = request.POST.get("phone") pwd_base64 = base64.base64_decode_str(base64, request.POST.get("pwd"))
create_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") pwd = make_password(pwd_base64)
last_login_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") if config.getconfig("isCypher"): # 启用加密数据库
print(username, pwd, email, phone) if config.getconfig("CypherMethod") == "caesar": # 加密方式为Caesar
avatar = request.FILES.get("avatar") username = caesar.caesar_encode(request.POST.get("username"))
auth_models.User.objects.create( email = caesar.caesar_encode(request.POST.get("email"))
username=username, phone = caesar.caesar_encode(request.POST.get("phone"))
pwd=pwd, else: # 加密方式为Base64
email=email, username = base64.base64_encode_str(base64, request.POST.get("username")).decode('utf-8')
phone=phone, email = base64.base64_encode_str(base64, request.POST.get("email")).decode('utf-8')
create_time=create_time, phone = base64.base64_encode_str(base64, request.POST.get("phone")).decode('utf-8')
last_login_time=last_login_time, auth_models.User.objects.using("cypher").create(
avatar=avatar username=username,
) pwd=pwd,
return HttpResponse("添加用户成功") email=email,
phone=phone,
create_time=create_time,
last_login_time=last_login_time,
avatar=avatar
)
else: # 不加密的数据库
username = request.POST.get("username")
email = request.POST.get("email")
phone = request.POST.get("phone")
auth_models.User.objects.using("default").create(
username=username,
pwd=pwd,
email=email,
phone=phone,
create_time=create_time,
last_login_time=last_login_time,
avatar=avatar
)
return HttpResponse("添加用户成功")
except Exception as e:
return HttpResponse(f"报错了:{e}")
@require_POST
def login_user(request):
"""
用户登录验证用户密码是否正确正确返回菜单错误返回用户名或密码不正确
如果使用加密版数据库则根据加密方式将用户名加密后进行数据库查询
:param request:
:return: "用户名或密码不正确"或用户拥有权限的菜单
"""
try:
pwd_input = base64.base64_decode_str(base64, request.POST.get("pwd"))
if config.getconfig("isCypher"): # 启用加密数据库
if config.getconfig("CypherMethod") == "caesar": # 加密方式为Caesar
username = caesar.caesar_encode(request.POST.get("username"))
else: # 加密方式为Base64
username = base64.base64_encode_str(base64, request.POST.get("username")).decode('utf-8')
pwd_made = auth_models.User.objects.using("cypher").filter(username=username).first()
else: # 不加密的数据库
username = request.POST.get("username")
pwd_made = auth_models.User.objects.using("default").filter(username=username).first()
if pwd_made is not None:
if check_password(pwd_input, pwd_made.pwd):
return HttpResponse(True)
else:
return HttpResponse("用户名或密码不正确")
else:
return HttpResponse("用户名或密码不正确")
except binascii.Error as e:
return HttpResponse("base64解码失败")
except Exception as e:
return HttpResponse(f"报错了:{e}")

0
apps/home/__init__.py Normal file
View File

3
apps/home/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
apps/home/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class HomeConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.home'

View File

3
apps/home/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
apps/home/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

20
apps/home/views.py Normal file
View File

@ -0,0 +1,20 @@
from django.shortcuts import render, HttpResponse
from apps.api.common import CaesarCypherClass, Base64CypherClass
# Create your views here.
def home(request):
bs64 = Base64CypherClass()
s = request.GET.get('s')
print(s)
s_encode = bs64.base64_encode_str(bs64,s)
print(s_encode)
s_decode = bs64.base64_decode_str(bs64,s)
# s_encode = bs64.base64_encode_pic(bs64, s)
# if s_encode != "图片路径不存在":
# s_decode = bs64.base64_decode_pic(bs64, s_encode)
# else:
# s_decode = ''
# return HttpResponse(f"解密:{s_decode}")
return HttpResponse(f"加密:{s_encode}\n解密:{s_decode}")

Binary file not shown.

BIN
data/db_cypher.sqlite3 Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB