FastAPI 学习之路(二十九)使用(哈希)密码和 JWT Bearer 令牌的 OAuth2

简介: FastAPI 学习之路(二十九)使用(哈希)密码和 JWT Bearer 令牌的 OAuth2

既然我们已经有了所有的安全流程,就让我们来使用 JWT 令牌和安全哈希密码让应用程序真正地安全。


       关于 JWT

       

       它是一个将 JSON 对象编码为密集且没有空格的长字符串的标准。字符串看起来像这样:


eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c


它没有被加密,因此任何人都可以从字符串内容中还原数据。


但它经过了签名。因此,当你收到一个由你发出的令牌时,可以校验令牌是否真的由你发出。


通过这种方式,你可以创建一个有效期为 1 周的令牌。然后当用户第二天使用令牌重新访问时,你知道该用户仍然处于登入状态。

一周后令牌将会过期,用户将不会通过认证,必须再次登录才能获得一个新令牌。而且如果用户(或第三方)试图修改令牌以篡改过期时间,你将因为签名不匹配而能够发觉。


       我们看下如何实现?


一、安装python-json产生和校验JWT。


pip install python-jose

二、使用PassLib处理哈希密码


pip install passlib     
还需要安装
bcrypt pip install bcrypt


三、我们看下如何使用,以及思路


创建一个工具函数以哈希来自用户的密码。


然后创建另一个工具函数,用于校验接收的密码是否与存储的哈希值匹配。


再创建另一个工具函数用于认证并返回用户。


创建用于设定 JWT 令牌签名算法的变量 「ALGORITHM」,并将其设置为 "HS256"。


创建一个设置令牌过期时间的变量。


定义一个将在令牌端点中用于响应的 Pydantic 模型。


创建一个生成新的访问令牌的工具函数。


get_current_user使用的是 JWT 令牌解码,接收到的令牌,对其进行校验,然后返回当前用户。


如果令牌无效,立即返回一个 HTTP 错误。


使用令牌的过期时间创建一个 timedelta 对象。


创建一个真实的 JWT 访问令牌并返回它。


我们最后看下实现代码


from fastapi import FastAPI, Depends,status,HTTPException
from pydantic import BaseModel
from typing import Optional
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from datetime import datetime, timedelta
from passlib.context import CryptContext
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
fake_users = {
    "leizi": {
        "username": "leizi",
        "full_name": "leizishuoceshikaifa",
        "email": "leizi@leizi.com",
        "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
        "disabled": False
    }
}
app = FastAPI()
def fake_hash_password(password: str):
    return password
class Token(BaseModel):
    access_token: str
    token_type: str
class TokenData(BaseModel):
    username: Optional[str] = None
class User(BaseModel):
    username: str
    email: Optional[str] = None
    full_name: Optional[str] = None
    disabled: Optional[bool] = None
class UserInDB(User):
    hashed_password: str
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
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 authenticate_user(fake_db, username: str, password: str):
    user = get_user(fake_db, username)
    if not user:
        return False
    if not verify_password(password, user.hashed_password):
        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
def get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return UserInDB(**user_dict)
def fake_decode_token(token):
    user = get_user(fake_users, token)
    return user
def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="验证失败",
        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(fake_users, username=token_data.username)
    if user is None:
        raise credentials_exception
    return user
def get_current_active_user(current_user: User = Depends(get_current_user)):
    if current_user.disabled:
        raise HTTPException(status_code=400, detail="已经删除")
    return current_user
@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
    user = authenticate_user(fake_users, 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.username}, expires_delta=access_token_expires
    )
    return {"access_token": access_token, "token_type": "bearer"}
@app.get("/users/me")
def read_users_me(current_user: User = Depends(get_current_active_user)):
    return current_user


我们去测试下,效果,我们去在docs上使用Authorize,默认的密码是:secret


image.png

相关文章
|
4天前
|
Java 测试技术 数据安全/隐私保护
SpringCloud微服务之最全JWT学习教程03
SpringCloud微服务之最全JWT学习教程03
95 0
|
9月前
|
JSON 前端开发 数据库
学习nest.js中如何使用jwt进行身份验证,这一篇文章就够
学习nest.js中如何使用jwt进行身份验证,这一篇文章就够
|
4天前
|
JSON 安全 程序员
[JavaWeb]——JWT令牌技术,如何从获取JWT令牌
[JavaWeb]——JWT令牌技术,如何从获取JWT令牌
|
4天前
|
存储 JSON 算法
SpringBoot之JWT令牌校验
SpringBoot之JWT令牌校验
23 2
|
4天前
|
JSON 前端开发 Java
JWT解密:探秘令牌魔法与Java的完美交互
JWT解密:探秘令牌魔法与Java的完美交互
27 0
JWT解密:探秘令牌魔法与Java的完美交互
|
4天前
|
前端开发 数据安全/隐私保护
JWT令牌
JWT令牌
16 0
|
4天前
|
JSON 前端开发 Java
|
4天前
|
JSON 安全 Java
JWT令牌技术
JSON Web Token (JWT) 是一种安全的、自包含的信息传输格式,常用于身份验证和信息交换。它由Header、Payload和Signature三部分组成,其中Signature用于验证消息完整性和发送者身份。JWT包含用户信息,服务器登录后发送给客户端,客户端使用JWT证明身份访问受保护资源。在Java项目中,可以使用`java-jwt`库进行JWT的生成和解析。要开始使用JWT,需在Maven或Gradle中添加相关依赖,并实现生成和解析JWT的方法。此外,文中还提供了一个简单的Java Web应用示例,展示如何在用户登录和访问受保护资源时使用JWT。
46 0
|
4天前
|
前端开发 Java Spring
SpringBoot通过拦截器和JWT令牌实现登录验证
该文介绍了JWT工具类、匿名访问注解、JWT验证拦截器的实现以及拦截器注册。使用`java-jwt`库生成和验证JWT,JwtUtil类包含generateToken和verifyToken方法。自定义注解`@AllowAnon`允许接口匿名访问。JwtInterceptor在Spring MVC中拦截请求,检查JWT令牌有效性。InterceptorConfig配置拦截器,注册并设定拦截与排除规则。UserController示例展示了注册、登录(允许匿名)和需要验证的用户详情接口。
246 1
|
4天前
JWT令牌的使用
JWT令牌的使用
57 0