Location>code7788 >text

Differences and specific uses of PyJWT and python-jose when dealing with JWT token processing

Popularity:951 ℃/2024-08-27 11:05:51

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

  1. FastAPI: for building fast, modern Web APIs.
  2. Request: FromFastAPI is imported from the client's IP address.
  3. JWT Configuration: Use constants to configure the JWT key, issuer, audience, encryption algorithm, and expiration time.
  4. generate_token function (math.):
    • Building JWT'sclaimsThe 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 upexp field defines the expiration time of the JWT.
    • utilization Create and sign the JWT.
  5. 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 aHTTPExceptionIf 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 withFastAPI 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, the will 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())