26.FastAPI安全性

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了26.FastAPI安全性相关的知识,希望对你有一定的参考价值。

参考技术A 软件开发中,安全是永恒的话题,FastAPI作为一个优秀的Python Web开发框架,为用户提供了多种工具,帮助用户以标准的方式轻松快速地解决软件开发中的安全性。

FastAPI 的 fastapi.security 模块中为各种安全方案提供了一些工具,这些工具简化了这些安全机制的使用方法。

FastAPI提供的OAuth2PasswordBearer是使用 OAuth2的密码授权模式的Bearer Token(不记名 token) 。创建OAuth2PasswordBearer 实例需要接收URL作为参数。

客户端会向该 URL 通过表单的格式发送 username 和 password 参数,然后得到一个 token 值;OAuth2PasswordBearer 并不会创建相应的 URL 路径操作,只是指明了客户端用来获取 token 的目标 URL。

代码示例:

在上面的代码中, tokenUrl="token"指的token是相对 URL 。

此时访问,其返回结果:

上面的结果表明:访问的内容以及被保护,必须经过授权后才可以访问。

当获取到表单数据后,需要进行密码校验,一般情况下,我们都会考虑使用哈希密码,PassLib 是一个用于处理哈希密码的非常好的 Python 包,它支持许多安全哈希算法以及配合算法使用的实用程序。

具体passlib的使用方法可以查看其文档https://passlib.readthedocs.io/en/stable

下面的代码示例在上面代码的基础上增加用户登录及Token验证

启动应用并执行请求:

测试无效登录:

测试正常登录:

返回token,在Headers中使用token访问:

修改token后请求:

上面的代码如果去掉 await verify_token(token) 行,则:

curl -H "Authorization:Bearer u000010007" http://127.0.0.1:8000 -i

会得到返回结果,原因是默认情况下,OAuth2PasswordBearer只负责请求头中是否具有Authorization:Bearer,如果有就会执行相应的请求,所以,为了验证Token的正确性,需要每个方法都执行相应的验证代码。

本例只作为例子,在实际开发中不会直接拿用户ID作为Token,为了提高系统的安全性,需要使用 JWT。下面我们就介绍 JWT。

JWT是一个将 JSON 对象编码为密集且没有空格的长字符串的标准。 具体学习和了解 JWT,请参考 https://jwt.io。

需要提到的主要是 JWT中的sub,JWT 的规范中有一个 sub 键,值为该令牌的主题。使用它并不是必须的,但这是我们放置用户标识的地方,所以一般情况下,我们在sub中存放用户ID, 为了避免 ID 冲突,当为创建 JWT 令牌时,可以在 sub 键的值前加上前缀,例如 username:、userid:等。

在 Python 中生成和校验 JWT 令牌 ,可以使用PyJWT,也可以使用 python-jose 。我们在本例中使用 python-jose 来编写代码。

使用:

使用 JWT,需要在系统中添加一个SECRET_KEY变量,用于生成令牌,如:

以下代码在上面代码的基础上使用 JWT 令牌。

与前面的代码差别之处:

1.生成Token的函数:build_access_token

2.校验Token的函数:verify_token

3.登录函数:login

请求测试:

登录:

令牌访问:

错误的令牌访问:

在大部分应用程序中,当用户访问某个接口API的时候,都需要明确访问者的身份,所以在应用程序中需要随时获取当前用户,由于在 JWT 令牌的 sub 字段中已经保存了用户信息,所以获取当前用户只需要对令牌解码即可。

在上面的代码的基础上,增加两个函数,代码如下:

请求测试:

以上,我们完成了一个简单的安全性示例,FastAPI提供的安全性框架帮助我们节约了很多代码,但在实际开发中,我们常常使用微服务的方式来开发,对于鉴权最好设计独立的微服务进行处理。后面我们会展示一个采用FastAPI开发的鉴权微服务,以便在此基础上进行业务系统的开发。

FastAPI 学习之路请求体有多个参数如何处理?

系列文章:

  FastAPI 学习之路(一)fastapi--高性能web开发框架

  FastAPI 学习之路(二)

  FastAPI 学习之路(三)

  FastAPI 学习之路(四)

  FastAPI 学习之路(五)

      FastAPI 学习之路(六)查询参数,字符串的校验

  FastAPI 学习之路(七)字符串的校验

   FastAPI 学习之路(八)路径参数和数值的校验

请求体有多个参数如何处理?

  别的不多说,我们先写一个需求,然后演示下如何展示。

需求:写一个接口,传递以下参数,书本的名称,描述,价格,打折。
接口返回返回最后的价格

我们去看下代码如何实现

from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None
@app.put("/items")
def update_item(item: Optional[Item]):
    result={}
    if  item.tax is not None:
        total=item.price*item.tax
        result[\'price\'] = total
        result[\'name\']=item.name
        return result
    result[\'price\'] = item.price
    result[\'name\'] = item.name
    return result

那么我们测试下,最后是否实现了这个功能,当我们输入所有的参数的时候。

 

 

最后是在我们实际的打折上返回的。

           那么我们看下,我们不增加打折如何返回

 

 

没有打折就原价返回了名称和价格。

        如果默认给了None或者其他内容,这个参数就是可以选择增加或者不增加。但是没有给默认值的时候,就是必须传递的,否则会返回对应的错误,我们可以看下。假如我们不传递价格。

 

 

 我们可以看到没有默认值的参数就是一个必须的。不然接口会返回对应的错误。

        除了声明以上单个的,我们还可以声明多个请求体参数,比如我们可以在之前的需求,增加一个返回,要求返回作者,和作者的朝代。如何实现呢。

            

from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None
class User(BaseModel):
    username: str
    year: str
@app.put("/items")
async def update_item(item: Optional[Item],user:User):
    result={}
    if  item.tax is not None:
        total=item.price*item.tax
        result[\'price\'] = total
        result[\'name\']=item.name
        result[\'user\']=user
        return result
    result[\'price\'] = item.price
    result[\'name\'] = item.name
    result[\'user\']=user
    return result

那么我们看下接口的请求

 

 当我们增加打折。看下返回结果

 

 

  我们可以看下接口的返回。

        FastAPI 将自动对请求中的数据进行转换,因此 item 参数将接收指定的内容,user 参数也是如此。

        我们要想在增加一个键,在哪里出售,但是要作为请求体的另一个键进行处理,如何 实现呢

from typing import Optional

from fastapi import FastAPI,Body
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None
class User(BaseModel):
    username: str
    year: str
@app.put("/items")
async def update_item(item: Optional[Item],user:User,sell:str=Body(...)):
    result={}
    if  item.tax is not None:
        total=item.price*item.tax
        result[\'price\'] = total
        result[\'name\']=item.name
        result[\'user\']=user
        result[\'sell\']=sell
        return result
    result[\'price\'] = item.price
    result[\'name\'] = item.name
    result[\'user\']=user
    result[\'sell\'] = sell
    return result


我们可以看到如下

 

  假如我们把参数放在查询内,返回错误

 

 参数必须放在body内请求。

文章首发在公众号,欢迎关注。

以上是关于26.FastAPI安全性的主要内容,如果未能解决你的问题,请参考以下文章

JAVA的安全性怎么理解?为啥它是安全的?

如何保障OA办公系统安全性

如何提高SQL Server的安全性控制

为了保证软件的安全性,Java采用了那些措施?

Java项目中如何实现数据的安全性,比如安全锁之类的,求代码

容器管理的安全性、Spring 安全性和身份验证