Python FastApi 实现签名验证

大家在写后台接口时,都想要设计一个安全的,稳定的架构来支持各种业务,此文章介绍的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.mfbz.cn/a/886196.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【AI大模型】深入Transformer架构:编码器部分的实现与解析(上)

目录 &#x1f354; 编码器介绍 &#x1f354; 掩码张量 2.1 掩码张量介绍 2.2 掩码张量的作用 2.3 生成掩码张量的代码分析 2.4 掩码张量的可视化 2.5 掩码张量总结 &#x1f354; 注意力机制 3.1 注意力计算规则的代码分析 3.2 带有mask的输入参数&#xff1a; 3.…

FOCShield v2.0.4原理图

1.FOCShield v2.0.4原理图,开源原文件用AD制作。用 AD09可以打开。 主要部分为 1.电机驱动芯片部分 2.电流采样部分

Windows 环境下安装 Anaconda 并适配到 PowerShell 的保姆级教程

Anaconda Anaconda 是一个流行的 Python 数据科学和机器学习平台&#xff0c;它包括了 Conda 包管理器、Python 以及数百个用于科学计算的库和工具。Anaconda 旨在简化包和环境管理&#xff0c;使得安装、更新和管理软件包变得容易&#xff0c;同时也能够轻松创建和切换不同的P…

大数据毕业设计选题推荐-民族服饰数据分析系统-Python数据可视化-Hive-Hadoop-Spark

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

【Linux】命令管道

一、命名管道的介绍 之前的管道博客中介绍的是匿名管道&#xff0c;这个管道的应用的一个限制就是只能在具有公共祖先&#xff08;具有亲缘关系&#xff09;的进程间通信。 如果我们不想在不相关的进程之间交换数据&#xff0c;可以使用FIFO文件来做这项工作&#xff0c;他经常…

输入捕获模式测频率PWMI模式测频率占空比

前沿知识&#xff1a;TIM输入捕获-CSDN博客 输入捕获相关函数 // 初始化输入捕获单元 // ICInit是4个通道共用一个函数的&#xff0c;第二个结构体参数&#xff0c;可以用来配置具体是哪个通道。 void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);/…

【MAUI】CollectionView之 垂直网格

App主页或者导航页面中动态按钮的垂直网格布局 在 XAML 中,CollectionView 可以通过将其 ItemsLayout 属性设置为 VerticalGrid,在垂直网格中显示其项: <CollectionView ItemsSource="{Binding Monkeys}"ItemsLayout

TCP/UDP初识

TCP是面向连接的、可靠的、基于字节流的传输层协议。 面向连接&#xff1a;一定是一对一连接&#xff0c;不能像 UDP 协议可以一个主机同时向多个主机发送消息 可靠的&#xff1a;无论的网络链路中出现了怎样的链路变化&#xff0c;TCP 都可以保证一个报文一定能够到达接收端…

【YOLO学习】YOLOv2详解

文章目录 1. 概述2. Better2.1 Batch Normalization&#xff08;批归一化&#xff09;2.2 High Resolution Classifier&#xff08;高分辨率分类器&#xff09;2.3 Convolutional With Anchor Boxes&#xff08;带有Anchor Boxes的卷积&#xff09;2.4 Dimension Clusters&…

二分查找算法专题(1)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; 优选算法专题 目录 二分查找算法的介绍 704. 二分查找 34. 在排序数组中查找元素的第一个和 最后一个位置 35. 搜索插入位置 69. x的平…

力扣题解 983

大家好&#xff0c;欢迎来到无限大的判断&#xff0c;祝大家国庆假期愉快 题目描述&#xff08;中等&#xff09; 最低票价 在一个火车旅行很受欢迎的国度&#xff0c;你提前一年计划了一些火车旅行。在接下来的一年里&#xff0c;你要旅行的日子将以一个名为 days 的数组给出…

Charles(青花瓷)抓取https请求

文章目录 前言Charles&#xff08;青花瓷&#xff09;抓取https请求 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&…

kafka下载配置

下载安装 参开kafka社区 zookeeperkafka消息队列群集部署https://apache.csdn.net/66c958fb10164416336632c3.html 下载 kafka_2.12-3.2.0安装包快速下载地址分享 官网下载链接地址&#xff1a; 官网下载地址&#xff1a;https://kafka.apache.org/downloads 官网呢下载慢…

2024/10/2 408 20题

c d d b b a b c b b a d c d a c

java基础 day1

学习视频链接 人机交互的小故事 微软和乔布斯借鉴了施乐实现了如今的图形化界面 图形化界面对于用户来说&#xff0c;操作更加容易上手&#xff0c;但是也存在一些问题。使用图形化界面需要加载许多图片&#xff0c;所以消耗内存&#xff1b;此外运行的速度没有命令行快 Wi…

【华为HCIP实战课程一】OSPF相关基础介绍及基础配置,网络工程师必修

一、OSPF介绍 开放式最短路径优先协议OSPF(Open Shortest Path First),IPv4使用的OSPFv2,针对IPv6使用OSPFv3协议。 二、为什么需要OSPF OSPF出现之前,网络广泛使用RIP路由协议,RIP由于最大16跳数限制无法适应大型网络,RIP是基于距离矢量算法的路由协议,应用在大型网…

过去8年,编程语言的流行度发生了哪些变化?PHP下降,Objective-C已过时

前天有一个汇总9个不同排名数据的“地表最强”编程语言排行榜&#xff0c;为了更好地理解语言流行度的变化&#xff0c;作者将2016年的类似调查结果与2024年的数据进行了比较。 虽然2016年的调查只包含6个排名&#xff0c;但它仍然提供了宝贵的参考数据。 我们来看看详细的情…

JSON的C实现(上)

JSON的C实现&#xff08;上&#xff09; JSON的C实现&#xff08;上&#xff09;前言JSON简介JSON的C实现思路小结 JSON的C实现&#xff08;上&#xff09; 前言 JSON是众多项目中较为常见的数据交换格式&#xff0c;为不同项目、系统间的信息交换提供了一个规范化标准。JSON…

Unity八股总结

这里写目录标题 OnEnable、Awake、Start运行时的发生顺序&#xff1f;哪些可能在同一个对象周期中反复的发生&#xff1f;动态加载资源的方式?Unity3d脚本从唤醒到销毁有着一套比较完整的生命周期&#xff0c;请列出系统自带的几个重要的方法。物理更新一般放在哪个系统函数里…

查找与排序-插入排序

排序算法可以分为内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#xff0c;而外部排序是因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;在排序过程中需要访问外存。常见的内部排序算法有&#xff1a;插入排序、希尔排序、选择排序…