在做大模型开发的过程中,相信很多小伙伴都是对大模型开发感兴趣,却对 fastapi 这个框架并不熟悉,但是,实际开发的项目确需要用户鉴权,这时候就会很头疼,查阅官方文档发现,官方虽然有例子,但是写的非常简单,只有一个自定义的verify_token函数,函数内容还需要自己实现,如果投入学习成本在 fastapi 的用户认证上就显得得不偿失。而 fastapi 是有现成的用户认证的框架的,比如 fastapi-users。今天,我就带领大家完成一个使用 fastapi-users来实现带有用户认证功能的Langserve接口。
以上是官方的认证鉴权的方法,实现过程需要自己来完成。
实现步骤:
下载fastapi-users
pipinstall'fastapi-users[sqlalchemy]'
代码目录结构
创建 langserve 项目的方法我就不再介绍了,感兴趣的同学,可以看我的这篇公众号文章:LangServe全面使用指南
.
├──app
│├──db.py
│├──__init__.py
│├──__pycache__
│├──schemas.py
│├──server.py
│└──users.py
├──Dockerfile
├──packages
│└──README.md
├──poetry.lock
├──pyproject.toml
├──README.md
└──test.db
其中,db.py、users.py、schemas.py都是新建的 python 文件,这个目录结构也是fastapi-users推荐的拆分方式。
代码编写
db.py
fromtypingimportAsyncGenerator
fromfastapiimportDepends
fromfastapi_users.dbimportSQLAlchemyBaseUserTableUUID,SQLAlchemyUserDatabase
fromsqlalchemy.ext.asyncioimportAsyncSession,async_sessionmaker,create_async_engine
fromsqlalchemy.ormimportDeclarativeBase
DATABASE_URL="sqlite+aiosqlite:///./test.db"
classBase(DeclarativeBase):
pass
classUser(SQLAlchemyBaseUserTableUUID,Base):
pass
engine=create_async_engine(DATABASE_URL)
async_session_maker=async_sessionmaker(engine,expire_on_commit=False)
asyncdefcreate_db_and_tables():
asyncwithengine.begin()asconn:
awaitconn.run_sync(Base.metadata.create_all)
asyncdefget_async_session()->AsyncGenerator[AsyncSession,None]:
asyncwithasync_session_maker()assession:
yieldsession
asyncdefget_user_db(session:AsyncSession=Depends(get_async_session)):
yieldSQLAlchemyUserDatabase(session,User)
schemas.py
importuuid
fromfastapi_usersimportschemas
classUserRead(schemas.BaseUser[uuid.UUID]):
pass
classUserCreate(schemas.BaseUserCreate):
pass
classUserUpdate(schemas.BaseUserUpdate):
pass
users.py
importuuid
fromtypingimportOptional
fromfastapiimportDepends,Request
fromfastapi_usersimportBaseUserManager,FastAPIUsers,UUIDIDMixin
fromfastapi_users.authenticationimport(
AuthenticationBackend,
BearerTransport,
JWTStrategy,
)
fromfastapi_users.dbimportSQLAlchemyUserDatabase
fromapp.dbimportUser,get_user_db
SECRET="SECRET"
classUserManager(UUIDIDMixin,BaseUserManager[User,uuid.UUID]):
reset_password_token_secret=SECRET
verification_token_secret=SECRET
asyncdefon_after_register(self,user:User,request:Optional[Request]=None):
print(f"User{user.id}hasregistered.")
asyncdefon_after_forgot_password(
self,user:User,token:str,request:Optional[Request]=None
):
print(f"User{user.id}hasforgottheirpassword.Resettoken:{token}")
asyncdefon_after_request_verify(
self,user:User,token:str,request:Optional[Request]=None
):
print(f"Verificationrequestedforuser{user.id}.Verificationtoken:{token}")
asyncdefget_user_manager(user_db:SQLAlchemyUserDatabase=Depends(get_user_db)):
yieldUserManager(user_db)
bearer_transport=BearerTransport(tokenUrl="auth/jwt/login")
defget_jwt_strategy()->JWTStrategy:
returnJWTStrategy(secret=SECRET,lifetime_seconds=3600)
auth_backend=AuthenticationBackend(
name="jwt",
transport=bearer_transport,
get_strategy=get_jwt_strategy,
)
fastapi_users=FastAPIUsers[User,uuid.UUID](get_user_manager,[auth_backend])
current_active_user=fastapi_users.current_user(active=True)
注意: 这些都是fastapi-users官方用例部分,最主要的是 server.py中的结合部分。
fromfastapiimportFastAPI,Depends
fromfastapi.responsesimportRedirectResponse
fromlangserveimportadd_routes
fromcontextlibimportasynccontextmanager
fromapp.dbimportUser,create_db_and_tables
fromapp.schemasimportUserCreate,UserRead,UserUpdate
fromapp.usersimportauth_backend,current_active_user,fastapi_users
fromlangchain.chat_modelsimportChatOpenAI
fromlangchain_community.chat_models.moonshotimportMoonshotChat
@asynccontextmanager
asyncdeflifespan(app:FastAPI):
#NotneededifyousetupamigrationsystemlikeAlembic
awaitcreate_db_and_tables()
yield
app=FastAPI(
lifespan=lifespan,
#dependencies=[Depends(current_active_user)]
)
app.include_router(
fastapi_users.get_auth_router(auth_backend),prefix="/auth/jwt",tags=["auth"]
)
app.include_router(
fastapi_users.get_register_router(UserRead,UserCreate),
prefix="/auth",
tags=["auth"],
)
app.include_router(
fastapi_users.get_reset_password_router(),
prefix="/auth",
tags=["auth"],
)
app.include_router(
fastapi_users.get_verify_router(UserRead),
prefix="/auth",
tags=["auth"],
)
app.include_router(
fastapi_users.get_users_router(UserRead,UserUpdate),
prefix="/users",
tags=["users"],
)
@app.get("/authenticated-route")
asyncdefauthenticated_route(user:User=Depends(current_active_user)):
return{"message":f"Hello{user.email}!"}
add_routes(
app,
MoonshotChat(),
path="/openai",
dependencies=[Depends(current_active_user)],
)
@app.get("/")
asyncdefredirect_root_to_docs():
returnRedirectResponse("/docs")
#Editthistoaddthechainyouwanttoadd
#add_routes(app,NotImplemented)
if__name__=="__main__":
importuvicorn
uvicorn.run(app,host="0.0.0.0",port=8000)
这里,我用的是kimi的api接口,最主要的改变是在add_routes中增加了dependencies参数,引入了fastapi-users封装好的current_active_user。
add_routes(
app,
MoonshotChat(),
path="/openai",
dependencies=[Depends(current_active_user)],#这句代码是关键
)
最后,看下实际调用 invoke 接口后的效果吧!只需要在调用的接口的 header 中加入Authorization即可,对应的值是bearer加 token。这个 token 是调用 login 接口后返回的。