为fastapi提供的接口,增加身份验证功能

添加身份验证后的接口能够防止非法用户写入内容,能够节省一定的磁盘空间:

main文件内容:

from fastapi import FastAPI,Request,Depends, HTTPException, status,Form
import os,pymysql
from fastapi.encoders import jsonable_encoder   ##将别的类型的数据装换成json类型的数据
from typing import Optional
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm   ##用于身份验证
from jose import JWTError, jwt      ##添加jwt令牌
from passlib.context import CryptContext    ##添加加密算法
from model.mysql import createtable,querydb,insertdb,query    ##导入数据库模块
from datetime import datetime, timedelta  
from pydantic import BaseModel  
app = FastAPI()
@app.get("/")
async def root():
    return {"message": "Hello Bigger Applications!"}
###定义加密信息
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
 
class Token(BaseModel):
    access_token: str
    token_type: str
 
class TokenData(BaseModel):
    username: Optional[str] = None
##定义用户模型
class User(BaseModel):
    id: Optional[int] = None
    username: Optional[str]=None
    userpwd: Optional[str]=None
    hashed_password: Optional[str]=None
 
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
app = FastAPI()
db = pymysql.connect("localhost", "root", "123456", "user")
##验证用户输入密码是否和数据库中的密码一致
def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)
 
##对用户密码进行加密
def get_password_hash(password):
    return pwd_context.hash(password)
##查询数据库中是否有该用户名的信息
def get_user(db, username: str):
    result=querydb(db,username)
    if result:
        return result
##判断该用户的密码是否正确
def authenticate_user(db, username: str, password: str):
    user= querydb(db, username)
    if not user:
        return False
    if not verify_password(password, user[3]):
        return False
    return user
 
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt
 
async def get_current_user(token: str = Depends(oauth2_scheme)):
    ##自定义异常类
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = TokenData(username=username)
    except JWTError:
        raise credentials_exception
    user = get_user(db, username=token_data.username)
    if user is ():
        raise credentials_exception
    return user
 
@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
    user = authenticate_user(db, form_data.username, form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user[1]}, expires_delta=access_token_expires
    )
    return {"access_token": access_token, "token_type": "bearer"}
 
###实现保存用户传入内容并落盘的api
@app.post("/content")
async def get_content(current_user: User = Depends(get_current_user),request:Request=None):
    data = await request.body()   #获得用户的body信息,将传入的内容赋值给data
    data = jsonable_encoder(data)   #将data装换为json格式
    dir=r'C:/Users/huangxinyi/Desktop/apicontenttest/'    ##定义保存用户传入内容的目录
    if not os.path.exists(dir):
        os.makedirs(dir)     ###建立文件目录
    filename = dir+current_user[1]    ###定义保存用户传入内容的文件名称,以用户的内容作为文件名
    with open(filename,'a') as f:
        f.write(data+'\n')
    # return data
 
###注册功能
@app.post("/register")
async def register(username: str = Form(...), password: str = Form(...)):
    user1=User()
    user1.username=username
    user1.userpwd=password
    user1.hashed_password=get_password_hash(password)
    db = pymysql.connect("localhost", "root", "123456", "user")
    try :    ##判断数据库中是否存在该user表
        query(db)
    except:
        createtable(db)
    try:
        results=insertdb(db, user1)
        print(results)
        if results=="用户名不能重复":
            raise HTTPException(status_code=400, detail="用户名不能重复")
        if results=='插入数据失败!':
            raise HTTPException(status_code=400, detail="注册用户失败")
    finally:
        db.close()

数据库的文件内容如下:

def createtable(db):
    # 使用cursor()方法获取操作游标
    cursor = db.cursor()
    cursor.execute("DROP TABLE IF EXISTS user")
    sql = """CREATE TABLE user (
            ID int PRIMARY KEY auto_increment,
            username VARCHAR(20) UNIQUE,
            userpwd VARCHAR(50),
            hashed_password VARCHAR(200))"""
    # 创建Sutdent表
    cursor.execute(sql)
##向数据库中插入数据
def insertdb(db,user):
    # 使用cursor()方法获取操作游标
    cursor = db.cursor()
    if querydb(db,user.username):
        return "用户名不能重复"
    # # SQL 插入语句
    sql = "INSERT INTO user(username, userpwd, hashed_password) \
       VALUES ('%s', '%s', '%s')" % \
       (user.username,user.userpwd,user.hashed_password)
    print(sql)
    try:
        # 执行sql语句
        cursor.execute(sql)
        # 提交到数据库执行
        db.commit()
    except:
        db.rollback()
        return '插入数据失败!'
##根据指定用户名去查询用户信息
def querydb(db,username):
    # 使用cursor()方法获取操作游标
    cursor = db.cursor()
    sql = "SELECT * FROM user WHERE username = '" +username + "'"
    print(sql)
    try:
        # 执行SQL语句
        cursor.execute(sql)
        # 获取所有记录列表
        results = cursor.fetchall()[0]
        return list(results)
    except:
        return False
 
##判断数据库是否为空
def query(db):
    cursor = db.cursor()
    sql = "SELECT * FROM user "
    cursor.execute(sql)
    results = cursor.fetchall()
    if results:
        return True
    else:
        return False
 
def closedb(db):
    db.close()

结果展示

可以通过交互式文档获得相应的语句,并且去执行post操作,实现保存用户传入内容落盘。
为fastapi提供的接口,增加身份验证功能_第1张图片
可以看到该api后面有一个锁的标志,即说明该api进行了身份验证的安全性操作,点击锁的标志,进行身份验证(在身份验证之前需要先创建一个合法的用户,点击register的api创建合法用户):
为fastapi提供的接口,增加身份验证功能_第2张图片创建合法用户之后就可以登录,才能验证成功:
为fastapi提供的接口,增加身份验证功能_第3张图片至此,用户已经成功登录,然后点击try it out获得其认证信息:
为fastapi提供的接口,增加身份验证功能_第4张图片
然后在cmd命令行中输入红色部分的内容,-d选项即为用户要传入的内容
在这里插入图片描述
可以看到在指定的文件目录下,已经存在了以该用户名为名字的文件:
为fastapi提供的接口,增加身份验证功能_第5张图片

你可能感兴趣的:(fastapi,python)