2024-10-24 09:23:39 +08:00

141 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding:utf-8 -*-
"""
@Author : xuxingchen
@Contact : xuxingchen@sinochem.com
@Desc : 登陆界面控制逻辑
"""
import hashlib
import os.path
import random
import io
import time
from typing import Optional
from starlette.responses import StreamingResponse
from pydantic import BaseModel
from fastapi import APIRouter, Query, Request
from PIL import Image, ImageDraw, ImageFont
from utils.database import get_table_handler
from utils import logger
from utils.misc import generate_captcha_text
from models.sessions import SessionsTable
router = APIRouter()
class LoginItem(BaseModel):
username: str
password: str
session_id: str
captcha: str
class LoginResp(BaseModel):
status: bool
message: str
token: Optional[str] = None
def generate_captcha_image(text, size=(120, 40), font_path='./data/mvboli.ttf', font_size=24):
"""生成验证码图片"""
image = Image.new('RGB', size, (73, 109, 137)) # 设置背景色
d = ImageDraw.Draw(image)
assert os.path.exists(font_path), "字体文件不存在"
font = ImageFont.truetype(font_path, font_size)
# d.text((5, 5), text, font=font, fill=(255, 255, 0)) # 设置字体颜色和位置
font_box = font.getbbox(text)
text_width, text_height = font_box[2] - font_box[0], font_box[3] - font_box[1]
text_x = (size[0] - text_width) // 2
text_y = (size[1] - text_height) // 2
d.text((text_x, text_y - 8), text, font=font, fill=(255, 255, 0))
# 添加噪点
for _ in range(100): # 可以根据需要调整噪点数量
x = random.randint(0, size[0] - 1)
y = random.randint(0, size[1] - 1)
image.putpixel((x, y), (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
# 添加线条
for _ in range(3): # 可以根据需要调整线条数量
x1, y1 = random.randint(0, size[0] - 1), random.randint(0, size[1] - 1)
x2, y2 = random.randint(0, size[0] - 1), random.randint(0, size[1] - 1)
d.line([(x1, y1), (x2, y2)], fill=(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)),
width=2)
# 将图片保存为字节流
bytes_io = io.BytesIO()
image.save(bytes_io, format='PNG')
# image.save("./data/tmp.png", format='PNG')
bytes_io.seek(0)
return bytes_io
def hash_password(password):
"""对密码进行 SHA-256 哈希加密"""
password_bytes = password.encode('utf-8')
sha256_hash = hashlib.sha256()
sha256_hash.update(password_bytes)
return sha256_hash.hexdigest()
def verify_password(password, hashed_password):
"""比对密码和哈希值"""
password_hash = hash_password(password)
return password_hash.lower() == hashed_password.lower()
def authenticate_token(token: str):
th = get_table_handler()
timeout_timestamp = str(int(time.time() * 1000 - 4 * 60 * 60 * 1000))
return SessionsTable.check_token(th, timeout_timestamp, token)
@router.get("/generateCaptcha", summary="生成验证码")
async def generate_captcha_endpoint(request: Request,
session_id: str = Query(None, description="Session ID")):
"""生成验证码图片并返回"""
if session_id:
captcha_text = generate_captcha_text()
logger.Logger.debug(f"{request.url.path} 验证码生成:{captcha_text}")
# session_id 入库
th = get_table_handler()
SessionsTable.insert(th, session_id, captcha_text)
captcha_image = generate_captcha_image(captcha_text)
# 设置HTTP响应的头部指定内容类型为PNG图片
headers = {"Content-Type": "image/png"}
return StreamingResponse(captcha_image, headers=headers)
else:
logger.Logger.error(f"{request.url.path} 请求参数缺少 session_id - {request.client.host}")
return {"status": False, "message": "请求参数缺少 session_id"}
@router.post("/login", response_model=LoginResp, response_model_exclude_none=True, summary="登录请求")
async def login(item: LoginItem):
"""简单的账户登录逻辑"""
status = False
token = None
th = get_table_handler()
# 验证码校验
right_captcha = SessionsTable.get_captcha(th, item.session_id)
if right_captcha is not None:
if item.captcha.lower() == right_captcha.lower():
# 账户密码校验
if item.username == "admin":
if verify_password(item.password, "B12AD23C7E230E2CA365508DD16635DD3D7214BCD9BEA27457A356FD5C15F8BF"):
status = True
message = "校验通过"
token = generate_captcha_text(16)
SessionsTable.update(th, item.session_id, item.username, token)
else:
message = "密码错误"
else:
message = "账户不存在"
else:
message = "验证码错误"
else:
message = "无效 session_id"
resp = LoginResp(status=status, message=message, token=token)
logger.Logger.debug("/login " + str(resp.__dict__))
return resp