【FastAPI后台API 八】JWT token认证登陆

JWT token认证登陆

前一篇博客讲述了获取和验证请求参数, 这一篇就实践下,演示一个最基础的JWT认证,我公司是用了两个token方式验证,一个请求token,一个刷新token,请求token过期时间短,专门用于请求数据,刷新token专门用于刷新过期请求token用的。

jwt官网 https://jwt.io/

如果还有不懂JWT的,就需要好好看看JWT的知识了,JWT认证目前是前后端分离中非常流行的一种认证方式: 由三段组成 第一段通常是加密算法,第二段是你存储的自定义信息(未加密任何人可以去https://jwt.io/看到数据) 第三段是 第一段和第二段生成的签名参数确保token没有被修改

** 更多关于FastAPI的文章,请关注个人网站 https://www.charmcode.cn/**

生成Token

依赖库

python 目前有好几个库实现jwt验证

  • python-jose
  • pyjwt
  • jwcrypto
  • authlib (ps:有幸在PyCon2019上海见过此库作者 github

这里不做对比演示,就随便选一个

pip install python-jose

简单的演示

from datetime import datetime, timedelta

from jose import jwt

# 加密密钥 这个很重要千万不能泄露了
SECRET_KEY = "kkkkk"

# 设置过期时间 现在时间 + 有效时间    示例5分钟
expire = datetime.utcnow() + timedelta(minutes=5)

# exp 是固定写法必须得传  sub和uid是自己存的值
to_encode = {"exp": expire, "sub": str(123), "uid": "12345"}

# 生成token 
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm="HS256")
print(encoded_jwt) 
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTU1MDg5MzQsInN1YiI6IjEyMyIsInVpZCI6IjEyMzQ1In0.lttAYe808lVQgGhL9NXei2bbC1LIGs-SS0l6qfU_QxU

可以复制去 https://jwt.io/ 解出来看看
在这里插入图片描述

解密token

payload = jwt.decode(
            "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTU1MDg5MzQsInN1YiI6IjEyMyIsInVpZCI6IjEyMzQ1In0.lttAYe808lVQgGhL9NXei2bbC1LIGs-SS0l6qfU_QxU",
            SECRET_KEY, algorithms="HS256"
        )
print(payload)
# {'exp': 1595508934, 'sub': '123', 'uid': '12345'}
正确的解密方式

上述方式是token什么都是正确的时候,而且还没有过期,就会正常解出来。现在加上常见的异常捕获。

from jose.exceptions import ExpiredSignatureError, JWTError
try:
    payload = jwt.decode(
                "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTU1MDk0ODQsInN1YiI6IjEyMyIsInVpZCI6IjEyMzQ1In0.deulPSOPfON-lfbXtvQfTfc-DwqvFoQqv7Y1BhMecBw",
                SECRET_KEY, algorithms="HS256"
            )
    print(payload)
# 当然两个异常捕获也可以写在一起,不区分
except ExpiredSignatureError as e:
    print("token过期")
except JWTError as e:
    print("token验证失败")

在FastAPI中实现JWT认证登陆

上述的jwt加密解密的过程搞清楚了,这一步就很简单了
首先创建一个security.py文件专门进行加密解密的

from datetime import datetime, timedelta
from typing import Any, Union, Optional
from jose import jwt
from fastapi import Header
# 导入配置文件
from setting import config

ALGORITHM = "HS256"


def create_access_token(
    subject: Union[str, Any], expires_delta: timedelta = None
) -> str:
    """
    # 生成token
    :param subject: 保存到token的值
    :param expires_delta: 过期时间
    :return:
    """
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(
            minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES
        )
    to_encode = {"exp": expire, "sub": str(subject)}
    encoded_jwt = jwt.encode(to_encode, config.SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


def check_jwt_token(
     token: Optional[str] = Header(...)
) -> Union[str, Any]:
    """
    解析验证 headers中为token的值 当然也可以用 Header(..., alias="Authentication") 或者 alias="X-token"
    :param token:
    :return:
    """

    try:
        payload = jwt.decode(
            token,
            config.SECRET_KEY, algorithms=[ALGORITHM]
        )
        return payload
    except (jwt.JWTError, jwt.ExpiredSignatureError, AttributeError):
        # 抛出自定义异常, 然后捕获统一响应
        raise custom_exc.TokenAuthError(err_desc="access token fail")

上面一定定义好了, 加密和解密token的方式,这一步来登陆生成token

# 从刚刚定义好jwt的文件导入生成方法
from security import create_access_token
from pydantic import BaseModel


class UserInfo(BaseModel):
    username: str
    password: str


@router.post("/login/access-token", summary="用户登录认证")
async def login_access_token(
        *,
        db: Session = Depends(deps.get_db),
        user_info: UserInfo,
) -> Any:
    """
    用户登录
    :param db:
    :param user_info:
    :return:
    """

    # 验证用户账号密码是否正确
     user = curd_user.authenticate(db, email=user_info.username, password=user_info.password)
    if not user:
        logger.info(f"用户邮箱认证错误: email{user_info.username} password:{user_info.password}")
        return response_code.resp_500(message="用户名或者密码错误")
    elif not curd_user.is_active(user):
        return response_code.resp_500(message="用户邮箱未激活")
    
    # 如果用户正确通过 则生成token
    # 设置过期时间
    access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)

    # 登录token 只存放了user.id
    return response_code.resp_200(data={
        "token": create_access_token(user.id, expires_delta=access_token_expires),
    })
验证token

这一步是使用 from fastapi import Depends 来验证 headers中的token
在上面security.py文件中有定义取headers中的token参数

from typing import Any, Union

from fastapi import Depends
# 从刚刚定义好jwt的文件导入解密方法
from security import check_jwt_token

@router.get("/user/info", summary="获取用户信息", response_model=user.UserInfo)
async def get_user_info(
	token_data: Union[str, Any] = Depends(check_jwt_token)  
) -> Any:
    """
    获取用户信息
    :param token_data:
    :return:
    """
    print(token_data)
    # 这个状态能响应说明token验证通过
    return response_code.resp_200(data={
        "username": "用户信息"
    })

所以正确的请求方式应该是这样的,在headers中携带token字段, 再次重述也可以再check_jwt_token方法中给token取别名,最常见的如Authentication
为什么不在check_jwt_token参数中直接写Authentication 了?
因为参数写成大写字母开头不符合python 编程pep8规范,还有就是X-Token的这种,变量不支持-符号,所以写成别名。

import requests

res = requests.get("http://127.0.0.1:8000/user/info", headers={
    "token": "xxxx",
    "content-type": "application/json"
})

总结

熟悉了前半部分的jwt token生成与解密的方式,就可以在任何Python框架(Django,Flask,Tornado,Sanic, Bottle等等)里面套用,很多封装的扩展,本质也是这个,一般不喜欢用扩展。

jwt认证其实非常简单,搞清楚加密解密的过程,原理稍微懂点就可以了,
后面结合redis, 可以管理登陆踢下线等操作。
还有就是token续签的问题,比如你正写博客了,博客还没写完token失效了,结果提交的时候token认证失败了,就需要token心跳检测续签了。

完整代码GitHub地址

github地址 https://github.com/wxy2077/fastapi-mysql-generator

  • 9
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 我可以给你一些提示来帮助你实现fastapijwt登录认证。首先,你需要安装fastapi和pyjwt库。然后,构建一个JWT认证函数,并将它添加到登录路由上。接下来,创建一个签名密钥,并将其添加到认证函数中。最后,将签名密钥添加到环境变量中,以便可以在登录路由中使用。 ### 回答2: FastAPI是一个基于Python的现代,高性能的Web框架,它提供了快速构建API和Web应用程序的能力。JWT(JSON Web Token)是一种用于在网络应用间传递声明的安全的、基于JSON的令牌。 要实现FastAPIJWT登录认证,需要使用以下步骤: 1. 导入所需的库: ```python from fastapi import FastAPI from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from passlib.context import CryptContext from jose import JWTError, jwt from datetime import datetime, timedelta ``` 2. 配置密码哈希和jwt密钥: ```python SECRET_KEY = "your_secret_key" ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/login") ``` 3. 创建用户模型和密码校验函数: ```python class User(BaseModel): username: str password: str def verify_password(plain_password, hashed_password): return pwd_context.verify(plain_password, hashed_password) ``` 4. 创建用户认证函数和生成JWT的函数: ```python async def authenticate_user(username: str, password: str): user = get_user_from_db(username) if not user: return False if not verify_password(password, user.password): return False return user def create_access_token(data: dict, expires_delta: timedelta): to_encode = data.copy() expire = datetime.utcnow() + expires_delta to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt ``` 5. 创建登录路由和保护需要认证的路由: ```python app = FastAPI() @app.post("/login") async def login(form_data: OAuth2PasswordRequestForm = Depends()): user = await authenticate_user(form_data.username, form_data.password) if not user: return {"error": "Invalid username or password"} 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("/protected") async def protected_route(token: str = Depends(oauth2_scheme)): try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise credentials_exception except JWTError: raise credentials_exception user = get_user_from_db(username) if user is None: raise credentials_exception return {"user": user} ``` 以上代码示例了一个快速实现的FastAPI JWT登录认证。在登录路由中,用户提供用户名和密码,然后校验用户名和密码是否正确,如果正确则生成一个JWT,并返回给用户。在保护的路由中,用户需要提供JWT作为认证凭证,服务器会验证JWT的有效性,并根据JWT中的信息返回相应的内容。 ### 回答3: FastAPI是一个Python的Web框架,它提供了快速构建API的能力。JWT(JSON Web Token)是一种用于身份验证和授权的开放标准。下面是一个使用FastAPI实现JWT登录认证的示例。 首先,需要安装FastAPI和PyJWT等必要的库。可以使用pip命令进行安装。 ```shell pip install fastapi pip install python-jose[cryptography] ``` 接下来,创建一个Python文件,例如`main.py`,并使用以下代码编写JWT登录认证的示例。 ```python from fastapi import FastAPI, Depends, HTTPException from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from jose import jwt, JWTError from datetime import datetime, timedelta from passlib.context import CryptContext app = FastAPI() SECRET_KEY = "your-secret-key" ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 fake_users_db = { 'username': { 'username': 'username', 'password': '$2b$12$Jl7haqJbg.fBx5AZvK7Hj.57A6FYRXTFmL/NyU.JvMhLDwwBUUz/e' # hashed_password: password } } pwd_context = CryptContext(schemes=['bcrypt'], deprecated='auto') oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") 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(username: str): if username in fake_users_db: user_dict = fake_users_db[username] return user_dict def authenticate_user(username: str, password: str): user = get_user(username) if not user: return False if not verify_password(password, user['password']): return False return user def create_access_token(data: dict, expires_delta: timedelta): to_encode = data.copy() expire = datetime.utcnow() + expires_delta to_encode.update({'exp': expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt @app.post("/token") async def login(form_data: OAuth2PasswordRequestForm = Depends()): user = authenticate_user(form_data.username, form_data.password) if not user: raise HTTPException(status_code=401, detail="Incorrect username or password") 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("/me") async def read_users_me(token: str = Depends(oauth2_scheme)): try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise HTTPException(status_code=401, detail="Invalid authentication credentials") except JWTError: raise HTTPException(status_code=401, detail="Invalid authentication credentials") user = get_user(username=username) if user is None: raise HTTPException(status_code=404, detail="User not found") return user ``` 上面的代码中,首先定义了一个用于存储用户信息的`fake_users_db`字典。然后使用`CryptContext`来进行密码的哈希操作。`verify_password`用于验证密码是否匹配,`get_password_hash`用于生成密码的哈希值。`get_user`用于返回指定用户名的用户信息。`authenticate_user`用于验证用户名和密码,并返回用户信息。`create_access_token`用于生成JWT令牌。 接下来,使用`@app.post`装饰器定义了一个`/token`的登录路由,接收用户名和密码,验证后生成并返回JWT令牌。 最后,使用`@app.get`装饰器定义了一个`/me`的路由,该路由需要进行JWT登录认证。在函数中解码JWT令牌,获取到用户信息并返回。 通过上述代码示例,我们实现了一个使用FastAPI实现JWT登录认证API。可以使用`uvicorn`命令运行该API。 ```shell uvicorn main:app ``` 接下来,可以通过向`http://localhost:8000/token`发起POST请求进行登录认证,获取到JWT令牌。然后使用该令牌向`http://localhost:8000/me`发起GET请求获取当前用户信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值