在之前的文章中,FastAPI 学习之路(二十九)使用(哈希)密码和 JWT Bearer 令牌的 OAuth2FastAPI 学习之路(二十八)使用密码和 Bearer 的简单 OAuth2FastAPI 学习之路(三十四)数据库多表操作,我们分享了基于jwt认证token和基于数据库创建用户,那么我们今天把这些代码整理下,形成基于数据库用户名密码,登陆验证token存储到redis中。

首先我们看下之前基于jwt认证token的代码

  1. from fastapi import Depends,status,HTTPException
  2. from pydantic import BaseModel
  3. from typing import Optional
  4. from jose import JWTError, jwt
  5. from datetime import datetime, timedelta
  6. from passlib.context import CryptContext
  7. SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
  8. ALGORITHM = "HS256"
  9. ACCESS_TOKEN_EXPIRE_MINUTES = 30
  10. # oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
  11. fake_users = {
  12. "leizi": {
  13. "username": "leizi",
  14. "full_name": "leizishuoceshikaifa",
  15. "email": "leizi@leizi.com",
  16. "hashed_password": "$2b$12$4grMcfV9UMijFC0CEeJOTuTHE21msQOmkUWuowUewRSXt8cimW/76",
  17. "disabled": False
  18. }
  19. }
  20. def fake_hash_password(password: str):
  21. return password
  22.  
  23. class Token(BaseModel):
  24. access_token: str
  25. token_type: str
  26. class TokenData(BaseModel):
  27. username: Optional[str] = None
  28. password:Optional[str]=None
  29. class User(BaseModel):
  30. username: str
  31. email: Optional[str] = None
  32. full_name: Optional[str] = None
  33. disabled: Optional[bool] = None
  34. class UserInDB(User):
  35. hashed_password: str
  36. pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
  37. def verify_password(plain_password, hashed_password):
  38. return pwd_context.verify(plain_password, hashed_password)
  39. def get_password_hash(password):
  40. return pwd_context.hash(password)
  41. def authenticate_user(fake_db, username: str, password: str):
  42. user = get_user(fake_db, username)
  43. print(get_password_hash(password))
  44. if not user:
  45. return False
  46.  
  47. if not verify_password(password, user.hashed_password):
  48. return False
  49. return user
  50. def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
  51. to_encode = data.copy()
  52. if expires_delta:
  53. expire = datetime.utcnow() + expires_delta
  54. else:
  55. expire = datetime.utcnow() + timedelta(minutes=15)
  56. to_encode.update({"exp": expire})
  57. encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
  58. return encoded_jwt
  59. def get_user(db, username: str):
  60. if username in db:
  61. user_dict = db[username]
  62. return UserInDB(**user_dict)
  63. def fake_decode_token(token):
  64. user = get_user(fake_users, token)
  65. return user
  66.  
  67. def get_current_user(token: str = Depends()):
  68. credentials_exception = HTTPException(
  69. status_code=status.HTTP_401_UNAUTHORIZED,
  70. detail="验证失败",
  71. headers={"WWW-Authenticate": "Bearer"},
  72. )
  73. try:
  74. payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
  75. username: str = payload.get("sub")
  76. if username is None:
  77. raise credentials_exception
  78. token_data = TokenData(username=username)
  79. except JWTError:
  80. raise credentials_exception
  81. user = get_user(fake_users, username=token_data.username)
  82. if user is None:
  83. raise credentials_exception
  84. return user
  85. def get_current_active_user(current_user: User = Depends(get_current_user)):
  86. if current_user.disabled:
  87. raise HTTPException(status_code=400, detail="已经删除")
  88. return current_user
  89. @app.post("/login", response_model=Token)
  90. async def login_for_access_token( tokendata:TokenData):
  91. user = authenticate_user(fake_users,tokendata.username,tokendata.password)
  92. if not user:
  93. raise HTTPException(
  94. status_code=status.HTTP_401_UNAUTHORIZED,
  95. detail="Incorrect username or password",
  96. headers={"WWW-Authenticate": "Bearer"},
  97. )
  98. access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
  99. access_token = create_access_token(
  100. data={"sub": user.username}, expires_delta=access_token_expires
  101. )
  102. return {"access_token": access_token, "token_type": "bearer"}

我们需要把这部分代码进行调整,我们调整到routers中的user.py。其实就是把之前的方法去柔和到新的方法中,需要调整下之前的用户创建,把登陆给实现了。

我们看下新修改后的代码。

  1. from fastapi import APIRouter,status
  2. from fastapi import Depends,HTTPException
  3. from models.crud import *
  4. from get_db import get_db
  5. from datetime import timedelta,datetime
  6. from jose import JWTError, jwt
  7. usersRouter=APIRouter()
  8. SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
  9. ALGORITHM = "HS256"
  10. ACCESS_TOKEN_EXPIRE_MINUTES = 30
  11.  
  12. from passlib.context import CryptContext
  13. pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
  14. def verify_password(plain_password, hashed_password):
  15. return pwd_context.verify(plain_password, hashed_password)
  16. def get_password_hash(password):
  17. return pwd_context.hash(password)
  18. # 新建用户
  19. @usersRouter.post("/users/", tags=["users"], response_model=Users)
  20. def create_user(user: UserCreate, db: Session = Depends(get_db)):
  21. """
  22. - **email**: 用户的邮箱
  23. - **password**: 用户密码
  24. """
  25. db_crest = get_user_emai(db, user.email)
  26. user.password=get_password_hash(user.password)
  27. if not db_crest:
  28. return db_create_user(db=db, user=user)
  29. raise HTTPException(status_code=200, detail="账号不能重复")
  30. def create_access_token(data: dict):
  31. to_encode = data.copy()
  32. encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
  33. return encoded_jwt
  34. def get_cure(token):
  35. credentials_exception = HTTPException(
  36. status_code=status.HTTP_401_UNAUTHORIZED,
  37. detail="验证失败",
  38. headers={"WWW-Authenticate": "Bearer"},
  39. )
  40. try:
  41. payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
  42. username: str = payload.get("sub")
  43. if username is None:
  44. raise credentials_exception
  45. return username
  46. except JWTError:
  47. raise credentials_exception
  48.  
  49. @usersRouter.post("/login",response_model=UsersToken)
  50. def login(user:UserCreate,db: Session = Depends(get_db)):
  51. db_crest = get_user_emai(db, user.email)
  52. if not db_crest:
  53. raise HTTPException(status_code=200, detail="账号不存在")
  54. pass

现在登陆还未完全实现,我们去实现下这块。

这里的UsersToken在schemas中实现。

  1. class UsersToken(UserBase):
  2. token: str

登陆的实现我们实现如下

  1. @usersRouter.post("/login",response_model=UsersToken)
  2. async def login(request: Request,user:UserCreate,db: Session = Depends(get_db)):
  3. #查看用户是否存在
  4. db_crest = get_user_emai(db, user.email)
  5. if not db_crest:
  6. raise HTTPException(status_code=200, detail="账号不存在")
  7. #校验密码
  8. verifypassowrd=verify_password(user.password,db_crest.password)
  9. if verifypassowrd:
  10. #产生token
  11. token=create_access_token(data={"sub": user.email})
  12. useris=await request.app.state.redis.get(user.email)
  13. if not useris:
  14. request.app.state.redis.set(user.email,token,expire=ACCESS_TOKEN_EXPIRE_MINUTES*60)
  15. usertoken=UsersToken(token=token,email=user.email)
  16. return usertoken
  17. raise HTTPException(status_code=200, detail="请勿重复登陆")
  18. else:
  19. raise HTTPException(status_code=200, detail="密码错误")

redis相关的还是在我们上次分享的时候的FastAPI 学习之路(五十四)操作Redis

我们去启动下去测试下,看我们实现的是否正确。

由于我们更新了我们的创建用户的时候的密码的hash呢,我们先去创建用户

接下来,我们调用我们的登录

发现登陆报错了。

这里我们在设计数据库的时候用的是hashed_password存储的密码,我们这里需要修改下

  1. verifypassowrd=verify_password(user.password,db_crest.hashed_password)

然后我们在测试下

这样我们的token就产生了,我们也在redis有了存储

那么接下来会分享如何校验token?

通过本次的分享,我们讲登陆的用户存储到了数据库中,讲登陆后的产生的token我们存储到了redis上了。这样我们的存储持久化,接下来,我会分享如何校验token做判断是否登陆。

所有的代码,都会放在gitee上,大家可以后续看到完整的代码。后续将开发几个接口,和结合我们的接口测试来分享。欢迎持续关注。

  1. https://gitee.com/liwanlei/fastapistuday

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

FastAPI 学习之路(五十六)将token存放在redis的更多相关文章

  1. FastAPI 学习之路(十六)Form表单

    系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...

  2. FastAPI 学习之路(十八)表单与文件

    系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...

  3. FastAPI 学习之路(十九)处理错误

    系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...

  4. FastAPI 学习之路(十五)响应状态码

    系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...

  5. FastAPI 学习之路(十二)接口几个额外信息和额外数据类型

    系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...

  6. FastAPI 学习之路(十四)响应模型

    系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...

  7. FastAPI 学习之路(十)请求体的字段

    系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...

  8. Kubernetes学习之路(十六)之存储卷

    目录 一.存储卷的概念和类型 二.emptyDir存储卷演示 三.hostPath存储卷演示 四.nfs共享存储卷演示 五.PVC和PV的概念 六.NFS使用PV和PVC 1.配置nfs存储 2.定义 ...

  9. Vue学习之路第十六篇:车型列表的添加、删除与检索项目

    又到了大家最喜欢的项目练习阶段,学以致用,今天我们要用前几篇的学习内容实现列表的添加与删除. 学前准备: ①:JavaScript中的splice(index,i)方法:从已知数组的index下标开始 ...

随机推荐

  1. SVN无法查看最近日志和提交记录

    现象: 使用SVN查看最近的提交记录日志时,最近总是无法显示出全部的日志内容,只能显示到几天之前的日志.就算是自己刚提交的代码也是无法没有记录的. 解决方式:右键选择TortoiseSVN中的&quo ...

  2. 关于JDK高版本下RMI、LDAP+JNDI bypass的一点笔记

    1.关于RMI 只启用RMI服务时,这时候RMI客户端能够去打服务端,有两种情况,第一种就是利用服务端本地的gadget,具体要看服务端pom.xml文件 比如yso中yso工具中已经集合了很多gad ...

  3. wpf内存泄漏问题

    http://www.cnblogs.com/Cindys/archive/2012/05/17/2505893.html http://blogs.msdn.com/b/jgoldb/archive ...

  4. CodeForce-811C Vladik and Memorable Trip(动态规划)

    Vladik and Memorable Trip CodeForces - 811C 有一个长度为 n 的数列,其中第 i 项为 ai. 现在需要你从这个数列中选出一些互不相交的区间,并且保证整个数 ...

  5. 【简单数据结构】链表--洛谷P1160

    题目描述 一个学校里老师要将班上NN个同学排成一列,同学被编号为1\sim N1∼N,他采取如下的方法: 先将11号同学安排进队列,这时队列中只有他一个人: 2-N2−N号同学依次入列,编号为i的同学 ...

  6. PHP中操作任意精度大小的GMP扩展学习

    对于各类开发语言来说,整数都有一个最大的位数,如果超过位数就无法显示或者操作了.其实,这也是一种精度越界之后产生的精度丢失问题.在我们的 PHP 代码中,最大的整数非常大,我们可以通过 PHP_INT ...

  7. windows 中cmd一些特殊命令

    chcp 65001  就是换成UTF-8代码页 chcp 936 可以换回默认的GBK chcp 437 是美国英语 shutdown -s -t 60   60秒后关机 shutdown /a  ...

  8. Kotlin协程基础

    开发环境 IntelliJ IDEA 2021.2.2 (Community Edition) Kotlin: 212-1.5.10-release-IJ5284.40 我们已经通过第一个例子学会了启 ...

  9. javascript 编码规范 - 正确使用parseInt

    题目描述 修改 js 代码中 parseInt 的调用方式,使之通过全部测试用例 示例1 输入 '12' 输出 12 示例2 输入 复制 '12px' 输出 复制 12 示例3 输入 '0x12' 输 ...

  10. python学习笔记(五)-文件操作2

    一.文件修改 现有文件file.txt,内容如下:二十四节气歌春雨惊春清谷天,夏满芒夏暑相连.秋处露秋寒霜降,冬雪雪冬小大寒.上半年逢六廿一,下半年逢八廿三.每月两节日期定,最多相差一二天.要求:将文 ...