PyJWT
cap (a poem)python-jose
are two Python libraries for working with JSON Web Tokens (JWTs). They both help generate, decode, validate, and manage JWTs, but they have some important differences in functional scope and design philosophy. This article describes some of the differences between them, as well as using FastAPI+ in your projects.python-jose
to handle access token generation and some example code for reference.
1、PyJWT
cap (a poem)The python-jose difference
PyJWT
PyJWT is a Python library that specializes in JWTs and is designed to simplify the creation and validation of JWTs.
Features:
- Specialized in JWT: PyJWT is specialized for JWT processing and does not provide other types of encryption or signing capabilities.
- simple and easy to use: PyJWT provides simple APIs for creating and validating JWTs.
- Support for common signature algorithms: Includes HMAC (HS256, HS384, HS512) and RSA (RS256, RS384, RS512).
- light-weight class (in athletics): Because PyJWT is focused on JWT, it has few dependencies and is easy to install and use.
Key Usage Examples:
import jwt import datetime # Create a JWT payload = { "user_id": 123, "exp": () + (hours=1) } secret = 'your-secret-key' token = (payload, secret, algorithm='HS256') # Decoding JWT decoded = (token, secret, algorithms=['HS256']) print(decoded)
python-jose
python-jose It is a broader cryptographic library that supports not only JWT, but also several JOSE (JSON Object Signing and Encryption) standards, including JWS (JSON Web Signature), JWE (JSON Web Encryption), JWK (JSON Web Key), JWA (JSON Web Key), JWK (JSON Web Algorithms), and JWA (JSON Web Algorithms).
Features:
-
Full JOSE Support:: In addition to JWT.
python-jose
Other JOSE standards are also supported, making it more powerful and flexible. - Multiple encryption and signature algorithms: Supports more algorithms than PyJWT, such as direct encryption (dir), symmetric-key encryption (A256KW, A192KW, A128KW), and multiple modes of RSA and ECDSA.
- Rich functionality: Provides finer-grained control and is suitable for application scenarios that require complex encryption and signature operations.
-
relatively complex:: As a result of broader functionality.
python-jose
is more complex to use than PyJWT.
Key Usage Examples:
from jose import jwt from import JWTError # Create a JWT payload = { "user_id": 123, "exp": () + (hours=1) } secret = 'your-secret-key' token = (payload, secret, algorithm='HS256') # Decoding JWT try: decoded = (token, secret, algorithms=['HS256']) print(decoded) except JWTError as e: print(f"Token is invalid: {e}")
Summary of differences
-
Scope of Functions:
PyJWT
Focus on JWT , suitable for projects that require simple JWT processing ;python-jose
It supports the entire JOSE standard and is suitable for projects that require more complex encryption and signing operations. -
usability:
PyJWT
The API is simple and easy to get started;python-jose
More powerful, but also more complex. -
Algorithm support:
python-jose
A wider range of algorithms are supported, which is especially advantageous when advanced encryption or signature scenarios are required. -
Usage Scenarios: If your project only needs to generate and validate JWTs.
PyJWT
is a good choice; if you need full JOSE support, including JWS, JWE, etc., or complex encryption and signing, thepython-jose
is the better choice.
2. Usepython-jose
Processing JWT
in usingpython-jose
Catching and handling exceptions is an important aspect when dealing with JWT.
1) mountingpython-jose
First, make sure you have installed thepython-jose
:
pip install python-jose
2)utilizationpython-jose
JWT Module
The following is an example of how to use thepython-jose
example of JWT processing, including how to catch exceptions:
from jose import jwt, JWTError from import ExpiredSignatureError # Defining keys and payloads secret = 'your-secret-key' payload = { "user_id": 123, "exp": () + (hours=1) } # Generate JWT token = (payload, secret, algorithm='HS256') # Decoding the JWT and handling possible exceptions try: decoded = (token, secret, algorithms=['HS256']) print(decoded) except ExpiredSignatureError: print("Token has expired") except JWTError: print("Token is invalid")
in dealing withpython-jose
When working with JWT, catching and handling exceptions correctly is key. Ensuring that your environment and tools can correctly recognize exception types will help you better manage JWT errors.
If we need to carry more information in the JWT playload, we can declare the keys in the claim.python-jose
cap (a poem)FastAPI
to implement the JWT token generation operation. The code is shown below.
utilizationFastAPI
cap (a poem)python-jose
Generate JWT tokens:
from fastapi import FastAPI, Depends, HTTPException, status, Request from import JSONResponse from jose import JWTError, jwt from datetime import datetime, timedelta app = FastAPI() # Configuration item, usually loaded from a configuration file JWT_SECRET_KEY = 'your_jwt_secret_key' JWT_ISSUER = 'your_issuer' JWT_AUDIENCE = 'your_audience' JWT_EXPIRED_DAYS = 7 ALGORITHM = 'HS256' def generate_token(user_info: dict, role_type: str): # Get IP address ip = user_info.get('ip', '') # declaration of definition claims = { 'id': user_info['id'], 'email': user_info['email'], 'name': user_info['name'], 'nickname': user_info.get('nickname', ''), 'phone_number': user_info.get('mobile_phone', ''), 'gender': user_info.get('gender', ''), 'full_name': user_info.get('full_name', ''), 'company_id': user_info.get('company_id', ''), 'company_name': user_info.get('company_name', ''), 'dept_id': user_info.get('dept_id', ''), 'dept_name': user_info.get('dept_name', ''), 'role_type': role_type, 'ip': ip, 'mac_addr': '', # Unable to get Mac address 'channel': '' } # Define the token expiration time expiration = () + timedelta(days=JWT_EXPIRED_DAYS) # Creating a JWT token to_encode = { **claims, 'iss': JWT_ISSUER, 'aud': JWT_AUDIENCE, 'exp': expiration } token = (to_encode, JWT_SECRET_KEY, algorithm=ALGORITHM) return token
@('/token') async def get_token(request: Request): # Simulated user information user_info = { 'id': 123, 'email': 'user@', 'name': 'John Doe', 'nickname': 'Johnny', 'mobile_phone': '123-456-7890', 'gender': 'Male', 'full_name': 'Johnathan Doe', 'company_id': 'ABC123', 'company_name': 'ABC Corp', 'dept_id': 'Dept001', 'dept_name': 'IT', 'ip': } role_type = 'Admin' token = generate_token(user_info, role_type) headers = { 'access-token': token, 'Authorization': f'Bearer {token}' } return JSONResponse(content={'token': token}, headers=headers) if __name__ == "__main__": import uvicorn (app, host="127.0.0.1", port=8000)
account for
-
FastAPI
: for building fast, modern Web APIs. -
Request
: FromFastAPI
is imported from the client's IP address. - JWT Configuration: Use constants to configure the JWT key, issuer, audience, encryption algorithm, and expiration time.
-
generate_token
function (math.):- Building JWT's
claims
The user's name and password will be displayed on the screen, which contains user information and additional fields such as IP address, role type, etc. - set up
exp
field defines the expiration time of the JWT. - utilization
Create and sign the JWT.
- Building JWT's
-
get_token
routing (in computer networks):- Simulates obtaining user information (including IP address) from a request.
- call (programming)
generate_token
Generate JWT. - Put the JWT in the response header and return it to the client.
3, in the api how to implement AllowAnonymous and verify authorization
In Python'sFastAPI
framework, you can implement a framework like the one inAllowAnonymous
and authorization verification functions.
1)Implementing the JWT Authorization Authentication Middleware
First, you need a dependency to check if the request contains a valid JWT token. If the token is invalid or missing, the dependency will reject the request. In this way, only routes marked as "allow anonymity" will skip validation.
2)Installation of dependencies
Ensure that you have installed thepython-jose
for processing JWT, andfastapi
:
3) Creating Authorized Dependencies
You can create a file namedget_current_user
dependency to validate the JWT token and extract user information. If not provided or if validation fails, throws theHTTPException
。
from fastapi import Depends, HTTPException, status from jose import JWTError, jwt from typing import Optional JWT_SECRET_KEY = 'your_jwt_secret_key' ALGORITHM = 'HS256' def get_current_user(token: str = Depends(oauth2_scheme)): try: payload = (token, JWT_SECRET_KEY, algorithms=[ALGORITHM]) user_id: str = ("id") if user_id is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) return payload except JWTError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, )
get_current_user
: Used to parse and validate the JWT, if the token is invalid or missing, it will throw aHTTPException
If you do not have a status code, a 401 status code will be returned.
For specific uses, we may cache user information in Redis to improve processing efficiency.
4)realizationAllowAnonymous
functionality
existFastAPI
In theDepends
to implement conditional authorization. For routes that require authorization, simply set theget_current_user
passed as a dependency to the routing function. Routes that do not require authorization, on the other hand, can be defined directly.
from fastapi import FastAPI, Depends, HTTPException, status from import OAuth2PasswordBearer app = FastAPI() oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") # Routes that allow anonymous access @("/public") def read_public_data(): return {"message": "This is a public endpoint"} # Routes requiring authorization @("/protected") def read_protected_data(current_user: dict = Depends(get_current_user)): return {"message": f"Hello, {current_user['name']}"} # Route for simulated login to generate JWT tokens @("/token") def login(): # Here the authentication process is omitted and a JWT token is generated directly token = ({"id": 1, "name": "John Doe"}, JWT_SECRET_KEY, algorithm=ALGORITHM) return {"access_token": token, "token_type": "bearer"}
-
Public routes (
/public
): Can be accessed directly without any authentication. -
Protected Route (
/protected
): A valid JWT token must be provided for access. -
Login Route (
/token
): Generate a JWT token that can be used on protected routes
Depends(get_current_user)
: In routes that need to be protected, inject through dependenciesget_current_user
, ensuring that only authenticated users have access.
existFastAPI
It is recommended that you inject the dependency via a dependency (Depends
) to get information about the current user, rather than directly accessing theThis approach is more flexible and compatible with the This approach is more flexible and compatible with
FastAPI
The design philosophy is more consistent.
In specific projects, we often define and handle the authorization process through middleware for convenience.
Verifying the validity of a user token is handled by the authentiate function.
We usually just add middleware processing to the entry again.
# JWT auth, required app.add_middleware( AuthenticationMiddleware, backend=JwtAuthMiddleware(), on_error=JwtAuthMiddleware.auth_exception_handler, )
4. Some error handling
When you encounter an "Invalid audience" error when decoding a JWT, it usually means that when generating or decoding the JWT, theaud
(audience) statement is not set or validated correctly. Below are the steps and instructions to resolve this issue:
1) understandingsaud
(Audience) Disclaimer
-
aud
is an optional declaration in a JWT that is typically used to specify the recipient (audience) of the JWT. When decoding a JWT, thewill check if this declaration matches the expected value.
Setup and inspectionaud
Statement.Set when generating a Tokenaud
If you want JWT to includeaud
statement, which can be passed when generating the token. Example:
to_encode = { "sub": "user_id", "aud": "your_audience", # Setting up the aud statement "exp": () + timedelta(minutes=30) } token = (to_encode, settings.TOKEN_SECRET_KEY, algorithm=settings.TOKEN_ALGORITHM)
Authentication when decoding the Tokenaud
When decoding the JWT, specify theaudience
parameter to match the generatedaud
:
try: payload = ( token, settings.TOKEN_SECRET_KEY, algorithms=[settings.TOKEN_ALGORITHM], audience="your_audience" # Verification of aud statement ) print(f"Decoded payload: {payload}") except JWTError as e: print(f"Token decode failed: {e}")
2)Token decode failed: Subject must be a string.
"Token decode failed: Subject must be a string" Errors are usually due tosub
(subject) The value of the declaration is not caused by a string.sub
is a declaration commonly used in JWT to identify the body of a token, such as a user ID or username.The JWT standard requires that thesub
value must be a string.
probesub
The value of the declaration
First, make sure that when generating the token, thesub
The value of the declaration is a string:
to_encode = { "sub": str(user_id), # Make sure user_id is a string "aud": "your_audience", # Other fields "exp": () + timedelta(minutes=30) } token = (to_encode, settings.TOKEN_SECRET_KEY, algorithm=settings.TOKEN_ALGORITHM)
in the event thatsub
value is a non-string type (such as an integer or other object), convert it to a string:
user_id = 123 # Assuming user_id is an integer to_encode = { "sub": str(user_id), # Convert user_id to a string "aud": "your_audience", "exp": () + timedelta(minutes=30) } token = (to_encode, settings.TOKEN_SECRET_KEY, algorithm=settings.TOKEN_ALGORITHM)
3) How schema removes sensitive fields
When working with data models and schemas (schemas), especially when sensitive information is involved (such as passwords, identities, etc.), it is sometimes necessary to remove these sensitive fields from the output or serialization results. Depending on the library or framework you are using, the approach to handling sensitive fields will vary. Here are some common ways to remove sensitive fields:
Using Pydantic'sexclude
parameters
If you are using Pydantic (e.g. in FastAPI), you can use the model'sdict
methodologicalexclude
parameter to exclude sensitive fields.
from pydantic import BaseModel class User(BaseModel): username: str email: str password: str # sensitive field user = User(username="user1", email="user1@", password="secret") # Creating a dictionary that does not contain sensitive fields user_dict = (exclude={"password"}) print(user_dict)
Using SQLAlchemy's__mapper_args__
If you are using SQLAlchemy and want to exclude sensitive fields from the serialization results, you can use the model's__mapper_args__
Perform the configuration. Example:
from sqlalchemy import Column, String from import declarative_base Base = declarative_base() class User(Base): __tablename__ = 'users' id = Column(String, primary_key=True) username = Column(String) email = Column(String) password = Column(String) # sensitive field def to_dict(self): # Remove sensitive fields return {: getattr(self, ) for c in self.__table__.columns if != 'password'} user = User(id="1", username="user1", email="user1@", password="secret") print(user.to_dict())
Custom Serialization Methods
If you are using a custom model or class and are not using a specific library, you can implement custom serialization methods to exclude sensitive fields.
class User: def __init__(self, username, email, password): = username = email = password # sensitive field def to_dict(self): # Remove sensitive fields return { "username": , "email": } user = User(username="user1", email="user1@", password="secret") print(user.to_dict())