Python FastApi 实现签名验证

news2024/10/3 8:23:00

大家在写后台接口时,都想要设计一个安全的,稳定的架构来支持各种业务,此文章介绍的Token的机制,和签名的验证。Token作为鉴权,签名作防篡改

目录

1.Token

2.签名

3.接口中的实现


1.Token

此处介绍的实现方式较简单,用的缓存来记录用户对应的Token,想要更完善一些的机制,可以使用数据库做记录,运行时做恢复读取处理。看实际项目规模来定

此处缓存的实现参考我的另一篇文章,有详细的介绍:Python Cache 实现缓存管理类_python缓存之importlib.cache的使用详解-CSDN博客

    # 创建数据库连接对象
    dao = UserDAO()
    # 根据登录名查询用户信息
    use = dao.GetUserByLogin(loginName=Account)
    if (use != None):
        # 验证密码是否一致
        if (use.Password == MD5(PassWord)):
            # 创建Token
            token = __createToken(dao, use)
            # 将生成的Token 保存到缓存中
            PyCache.set(use.UserCode, use)
            res = MsgModel(True, MsgErrorType.Success, "", token)
        else:
            # 返回错误信息
            res = MsgModel(False, MsgErrorType.Login_NameOrPwdError)
    else:
        # 返回错误信息
        res = MsgModel(False, MsgErrorType.Login_NameOrPwdError)

当用户登录成功后,会有相应的Token返回,前端就自己做登录状态记录。

请求头参数 和 Token的数据实例 参考如下:

class HeaderModel(BaseModelPY):
    UserCode :str #用户编码
    Timestamp :str #时间戳
    Nonce:str #随机数
    Signature:str #签名
    Permit:int #权限


#Token令牌
class TicketAuth(BaseModelPY):
    UserCode:str #用户编码
    Token:str #Token令牌
    CreateTime:datetime=None  #创建时间
    ExpireTime:datetime=None #过期时间
    RefreshToken:str #刷新Token令牌
    RefreshTokenExpireTime:datetime=None #刷新Token令牌 过期时间

Token的机制:

Token 不当做请求参数,但会参与签名的生成(下面会介绍)

时间戳用来判断请求时间,超过范围的视作无效的请求,也会参与签名的生成

随机数:作为参数,也会参与签名的生成

权限:一般作为权限阈值的判断,要不要使用就看项目定义了

Token有过期时效   后台判断过期后,则视为Token无效

RefreshToken的过期时效比Token要长,当Token过期后,可以使用RefreshToken进行刷新Token,前端可以自行做逻辑刷新(可以返回特定的Code作判断),从而让客户无感

当Token和RefreshToken都过期后,则需要重新登录了

2.签名

先定一个验证签名的方法  verify_signature

从请求头里边获取相应的信息,如用户编码、时间戳、随机数和签名信息

继而做一些逻辑判断,数据为空,时间戳超出范围等

用 用户编码 从缓存对像中获取后台保存的Token,判断Token的时效性

最后 后台通过缓存的Token 去生成一次签名,跟请求中的签名进行判断是否一致

签名的组装逻辑可以自行定义,比如下面的举例:

# 签名 UserCode+Timestamp+Nonce+Token 进行MD5加密 大写

将用户编码、时间戳、随机数和Token进行字符拼装,再进行MD5加密,随后转成大写

前端也是这么操作,后台也是如此生成 ;   前端通过登录时获取的Token去进行拼装,后台用缓存对象中的Token进行拼装;

这里我检讨一下:生成签名的数据应该还要包含 请求参数 才能防篡改,我这里是因为项目不对外开放,就写简单了。

实现方式就大概这么做,可以自行扩展

def verify_signature(request: Request) -> MsgModel:    
    try:
        # 从请求头中获取认证信息
        headerModel = HeaderModel()
        token: TicketAuth
        msg = MsgModel.Success()
        IsValid: bool = True

        # 通过request获取Header里边的数据模型
        headerModel.UserCode = request.headers.get("UserCode")  # 用户编码
        headerModel.Timestamp = request.headers.get("Timestamp")  # 时间戳
        headerModel.Nonce = request.headers.get("Nonce")  # 随机数
        headerModel.Signature = request.headers.get("Signature")  # 签名

        # Header数据不能为空
        if (IsValid and headerModel.UserCode == None or not headerModel.UserCode):
            IsValid = False
            msg = MsgModel(False, MsgErrorType.Header_Empty, "Header is empty")

        if (IsValid):
            # 验证时间戳
            # 将时间戳转换为日期时间对象
            ft = float(headerModel.Timestamp)/1000
            ts = datetime.fromtimestamp(timestamp=ft)
            # 时间计算差
            time_difference = datetime.now() - ts
            # 时间戳一分钟内有效
            if (time_difference.seconds > 300):
                IsValid = False
                msg = MsgModel(False, MsgErrorType.Request_TimeOut,
                               f"Request Time Out[{time_difference.seconds}]")

        if (IsValid):
            # 判断Token是否有效
            # 从缓存中取数据
            cache_key = headerModel.UserCode+"_Token"
            token = PyCache.get(cache_key)
            if (token is not None):
                # 判断Token是否过期
                if (token.ExpireTime < datetime.now()):
                    IsValid = False
                    msg = MsgModel(False, MsgErrorType.Token_Expired,
                                   "Token Have Expired")
            else:
                IsValid = False
                msg = MsgModel(False, MsgErrorType.Token_Expired,
                               "Token Have Expired")

        if (IsValid):
            # 验证用户请求是否合法
            # 签名 UserCode+Timestamp+Nonce+Token 进行MD5加密 大写
            strRaw = headerModel.UserCode+headerModel.Timestamp+headerModel.Nonce+token.Token
            sign = MD5(strRaw).upper()  # 变为大写
            # 验证参数中的签名是否一致
            if (headerModel.Signature != sign):
                IsValid = False
                msg = MsgModel(False, MsgErrorType.Sign_Invalid,
                               "The signature is invalid")
        if (IsValid):
            try:
                #从缓存数据获取用户数据 得到用户类型
                #获取用户类型相应的权限
                #再接口中判断是否足够的权限来调用
                user = PyCache.get(headerModel.UserCode)
                if (user is not None):
                    #获取用户类型的权限
                    dictList = PyCache.get("DictData")
                    dict = next(
                        (x for x in dictList if x.DictCode == user.UserType), None)
                    if (dict is not None):
                        headerModel.Permit = int(dict.DictValue)
                    else:
                        headerModel.Permit = 0
                else:
                    headerModel.Permit = 0
            except Exception as ex:
                headerModel.Permit = 0
                LogOperate.error("获取用户权限发生异常", ex)
                return MsgModel.Internal_Error()
        # headerModel.Permit=100
        msg.MsgContent = headerModel
        return msg
    except Exception as ex:
        LogOperate.error("签名验证发生异常", ex)
        return MsgModel.Internal_Error()

3.接口中的实现

最后再说下在每个接口中如何实现:

--- 无需签名验证的接口 : 比如登录接口

只需要定义入参 即可

@userLoginController.post(apiPrefix+'Login', name="登录-用户登录", response_model=None, summary="登录-用户登录")
async def user_login(Account: str = Form(...), PassWord: str = Form(...)):
    res = MsgModel.Failure()
    return res

--- 需要签名验证的接口:

一是定义入参,二是增加调用签名验证方法 verify_signature

下面的例子:

除了分页必要的参数 page、pageSize 等等,最后有个IsValid=Depends(verify_signature) 即是签名验证方法的调用

第一行就是作为判断签名验证是否通过了

如果签名验证失败了,记得要返回IsValid对象,否则接口会返回null

    if (IsValid.MsgSuccessed): 

    else:

        return IsValid

        

from fastapi import APIRouter, Depends
from datetime import datetime
from Modules.Models.MsgModel import MsgModel, MsgErrorType, MsgPageModel
from Modules.SysFrame.String import getApiPrefix, getVersion, IsNullOrEmpty, Guid, MD5
from Modules.SysFrame.GlobalData import verify_signature


@orderController.get(apiPrefix+'GetOrderPageList', name="Order-分页获取订单列表数据")
async def GetOrderPageList(page: int,  pageSize: int,  begin: datetime, end: datetime, orderId: str, customer: str, IsValid=Depends(verify_signature)):
    if (IsValid.MsgSuccessed):
        try:
            orderDao = OrderDAO()
            res = orderDao.GetOrderPageList(
                page, pageSize, begin, end, orderId, customer)

            if (res.IsSucessed):
                mList = res.Tag
                models = []
                for m in mList:
                    model = _Order_Entity_To_Model(m)
                    models.append(model.tojson())
                return MsgPageModel.Success(models, total=res.Total)
            else:
                return MsgPageModel.Failure(msgDes=res.Message)
        except Exception as ex:
            LogOperate.error("GetOrderPageList", ex)
            return MsgPageModel.Internal_Error()
    else:
        return IsValid

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2185864.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【技术详解】SpringMVC框架全面解析:从入门到精通(SpringMVC)

文章目录 【技术详解】SpringMVC框架全面解析&#xff1a;从入门到精通(SpringMVC)SpringMVC概述1. 三层架构与MVC架构区别1.1 三层架构1.2 MVC架构1.3前后端分离开发模式 2. SpringMVC环境搭建2.1 注解启动方式2.2 xml启动方式2.3 SpringMVC PostMan工具使用 3. SpringMVC 请求…

electron出现乱码和使用cmd出现乱码

第一种出现乱码。这种可以通过chcp 65001&#xff0c;设置为utf-8的编码。第二种&#xff0c;是执行exec的时候出现乱码&#xff0c;这个时候需要设置一些编码格式&#xff0c;可以通过iconv-lite进行解决&#xff0c;这个方法是node自带的&#xff0c;所以不需要导入。使用方法…

scrapy爬取汽车、车评数据【上】

这个爬虫我想分三期来写&#xff1a; ✅ 第一期写如何爬取汽车的车型信息&#xff1b; ✅ 第二期写如何爬取汽车的车评&#xff1b; ✅ 第三期写如何对车评嵌入情感分析结果&#xff0c;以及用简单的方法把数据插入mysql中&#xff1b; 技术基于scrapy框架、BERT语言模型、mysq…

【springboot】整合沙箱支付

目录 1. 配置沙箱应用环境2. 配置springboot项目1. 引入依赖2. 配置文件注册下载ngrok 3. 创建支付宝支付服务类4. 支付界面模板5. 控制类实现支付6. 测试 1. 配置沙箱应用环境 使用支付宝账号登录到开放平台控制台。 使用支付宝登录后&#xff0c;看到以下页面&#xff0c;下…

2、.Net 前端框架:OpenAuth.Net - .Net宣传系列文章

OpenAuth.Net 是一个开源的身份验证框架&#xff0c;由开发者 Yubaolee 创建&#xff0c;它旨在简化 Web 应用和服务的安全授权过程。这个框架以其强大的功能和易用性&#xff0c;为开发人员提供了一种高效的方式来处理用户认证和授权问题。 OpenAuth.Net 的关键特性包括&#…

无头双向不循环链表的模拟

总共由四部分组成&#xff0c;一个拥有总体方法名参数 的接口、一个异常类、一个方法类、一个测试类 先看我们写入的接口 public interface ILinkList {// 2、无头双向链表实现//头插法void addFirst(int val);//尾插法void addLast(int val);//任意位置插入,第一个数据节点为…

广联达 Linkworks办公OA Service.asmx接口存在信息泄露漏洞

漏洞描述 广联达科技股份有限公司以建设工程领域专业应用为核心基础支撑&#xff0c;提供一百余款基于“端云大数据”产品/服务&#xff0c;提供产业大数据、产业新金融等增值服务的数字建筑平台服务商。广联达OA存在信息泄露漏洞&#xff0c;由于某些接口没有鉴权&#xff0c…

Unity Input System自动生成配置

参考视频 创建及配置新输入系统 New Input System&#xff5c;Unity2022.2 最新教程《勇士传说》入门到进阶&#xff5c;4K_哔哩哔哩_bilibili ProjectSettings设置 Unity编辑器菜单栏选择Edit->Project Settings->Player->Other Settings,将Api Compatibility Level…

Ascend C 算子运行资源管理简介

Ascend C 算子运行资源管理简介 在 Ascend C 平台上开发深度学习应用时&#xff0c;运行资源的管理是一个核心知识点。通过合理管理算子执行中的计算资源&#xff0c;能够有效提升模型的执行效率和硬件利用率。本文将简要介绍 Ascend C 算子运行时的资源管理。 1. AscendCL 初…

draw.io创建自定义形状

Create custom shapes in draw.io using the text editor Reference draw怎么创建和编辑复杂的自定义形状 https://blog.csdn.net/u012028275/article/details/113828875 Create custom shapes in draw.io using the text editor

AMD发布首个AI小语言模型:6900亿token、推测解码提速3.88倍

AMD发布了自己的首个小语言模型(SLM)&#xff0c;名为“AMD-135M”。相比于越来越庞大的大语言模型(LLM)&#xff0c;它体积小巧&#xff0c;更加灵活&#xff0c;更有针对性&#xff0c;非常适合私密性、专业性很强的企业部署。 AMD-135小模型隶属于Llama家族&#xff0c;有两…

5分钟学会SPI

SPI 定义&#xff1a;SPI 是一种机制&#xff0c;允许用户在不修改现有代码的情况下扩展和替换特定服务的实现。它定义了一组接口&#xff08;Service Interfaces&#xff09;和一组实现&#xff08;Service Providers&#xff09;&#xff0c;使得应用程序可以动态加载和使用…

netty之Netty与SpringBoot整合

前言 在实际的开发中&#xff0c;我们需要对netty服务进行更多的操作&#xff0c;包括&#xff1b;获取它的状态信息、启动/停止、对客户端用户强制下线等等&#xff0c;为此我们需要把netty服务加入到web系统中。 MyChannelInitializer public class MyChannelInitializer ex…

[C++]使用C++部署yolov11目标检测的tensorrt模型支持图片视频推理windows测试通过

官方框架&#xff1a; https://github.com/ultralytics/ultralytics yolov8官方最近推出yolov11框架&#xff0c;标志着目标检测又多了一个检测利器&#xff0c;于是尝试在windows下部署yolov11的tensorrt模型&#xff0c;并最终成功。 重要说明&#xff1a;安装环境视为最基…

边缘自适应粒子滤波(Edge-Adaptive Particle Filter)的MATLAB函数示例,以及相应的讲解

目录 讲解 初始化 预测步骤 观测模拟 权重更新 重采样 状态估计 总结 下面是一个简单的边缘自适应粒子滤波&#xff08;&#xff09;的函数示例&#xff0c;以及相应的讲解。 程序源代码&#xff1a; function X_est edgeAdaptiveParticleFilter(numParticles, numS…

awd基础学习

一、常用防御手段 1、改ssh密码 passwd [user] 2、改数据库密码 进入数据库 mysql -uroot -proot 改密码 update mysql.user set passwordpassword(新密码) where userroot; 查看用户信息密码 select host,user,password from mysql.user; 改配置文件 &#xff08;否则会宕机…

笔记整理—linux进程部分(6)进程间通信、alarm和pause

两个进程间通信可能是任何两个进程间的通信&#xff08;IPC&#xff09;。同一个进程是在同一块地址空间中的&#xff0c;在不同的函数与文件以变量进程传递&#xff0c;也可通过形参传递。2个不同进程处于不同的地址空间&#xff0c;要互相通信有难度&#xff08;内存隔离的原…

2024-09-27 buildroot C和语言将 中文的GBK编码转换为 UTF-8 的代码, printf 显示出来,使用 iconv 库去实现。

一、GBK 的英文全称是 "Guobiao Kuozhan"&#xff0c;意为 "National Standard Extended"。它是对 GB2312 编码的扩展&#xff0c;用于表示更多汉字和符号 GBK&#xff08;国标扩展汉字编码&#xff09;是一种用于简体中文和繁体中文字符的编码方式&#x…

计算机毕业设计Python+Spark知识图谱酒店推荐系统 酒店价格预测系统 酒店可视化 酒店爬虫 酒店大数据 neo4j知识图谱 深度学习 机器学习

《PythonSpark知识图谱酒店推荐系统》开题报告 一、研究背景与意义 随着互联网技术的飞速发展和人们生活水平的提高&#xff0c;旅游和酒店行业迎来了前所未有的发展机遇。然而&#xff0c;面对海量的酒店信息和多样化的用户需求&#xff0c;如何快速、准确地为用户推荐符合其…

【Java】—— 集合框架:List接口常用方法与List接口的实现类

目录 4. Collection子接口1&#xff1a;List 4.1 List接口特点 4.2 List接口方法 4.3 List接口主要实现类&#xff1a;ArrayList 4.4 List的实现类之二&#xff1a;LinkedList 4.5 List的实现类之三&#xff1a;Vector 4.6 练习 4. Collection子接口1&#xff1a;List …