• How JWT (JSON Web Token) authentication works?


    How JWT (JSON Web Token) authentication works?

    https://www.idiotinside.com/2019/09/26/how-jwt-authentication-works/

    What is JWT (JSON Web Token)?

    JSON Web Token (JWT) is an open standard (RFC 7519) for securely transmitting information between parties as JSON object.

    It is compact, readable and digitally signed using a private key/ or a public key pair by the Identity Provider(IdP). So the integrity and authenticity of the token can be verified by other parties involved.

    The purpose of using JWT is not to hide data but to ensure the authenticity of the data. JWT is signed and encoded, not encrypted. 

    JWT is a token based stateless authentication mechanism. Since it is a client-side based stateless session, server doesn’t have to completely rely on a datastore(database) to save session information.

    How it works?

    Basically the identity provider(IdP) generates a JWT certifying user identity and Resource server decodes and verifies the authenticity of the token using secret salt / public key.

    1. User sign-in using username and password or google/facebook.
    2. Authentication server verifies the credentials and issues a jwt signed using either a secret salt or a private key.
    3. User’s Client uses the JWT to access protected resources by passing the JWT in HTTP Authorization header.
    4. Resource server then verifies the authenticity of the token using the secret salt/ public key.

    JWT

     

    reference

    https://jwt.io/introduction/

    DEMO

    https://github.com/amirhosss/FastAPI-RS256-MongoDB-Redis

    私钥签发 token

    https://github.com/amirhosss/FastAPI-RS256-MongoDB-Redis/blob/d3f484b542bb96be5d74b499a9f3ab586b77d4d1/core/security.py#L24

    import uuid
    from datetime import datetime, timedelta
    
    from jose import jwt
    from passlib.hash import bcrypt_sha256
    
    from .config import settings
    
    
    def get_password(password):
        return bcrypt_sha256.hash(password)
    
    
    def verify_password(plain_password, hashed_password):
        return bcrypt_sha256.verify(plain_password, hashed_password)
    
    
    with open('core/private_key.pem', 'rb') as private_file:
        private_key = private_file.read()
    with open('core/public_key.pem', 'rb') as public_file:
        public_key = public_file.read()
        
    
    def create_jwt_token(sub: str, aud: str, expires_delta: timedelta = None):
        expires_delta_condition = {
            'refresh': timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS),
            'access': timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES),
            'verification': timedelta(minutes=settings.VERIFICATION_TOKEN_EXPIRE_MINUTES)
        }
    
        if expires_delta:
            expire = datetime.utcnow() + expires_delta
        else:
            expire = datetime.utcnow() + expires_delta_condition[aud]
    
        to_encode = {'sub': sub, 'aud': aud, 'exp': expire, 'jti': str(uuid.uuid4())}
        encoded_jwt = jwt.encode(
            to_encode,
            private_key,
            algorithm=settings.JWT_TOKEN_ALGORITHM
        )
        return 

    公钥验证和打开token

    https://github.com/amirhosss/FastAPI-RS256-MongoDB-Redis/blob/master/api/dependencies/get_user.py

    from typing import Optional
    
    from fastapi import Depends, HTTPException, status, Cookie
    from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
    from pydantic.error_wrappers import ValidationError
    from jose import jwt
    from jose.exceptions import JWTError
    
    import crud, models, schemas
    from core.config import settings
    from core.security import public_key
    from utils.redis import redis
    
    security = HTTPBearer(auto_error=settings.HTTP_BEARER_AUTO_ERROR)
    
    
    class TokenRequired:
        def __init__(self, token_type) -> None:
            self.token_type = token_type
    
        def __call__(
            self,
            credentials: HTTPAuthorizationCredentials = Depends(security),
            access_token: Optional[str] = Cookie(None),
            refresh_token: Optional[str] = Cookie(None)
    
        ) -> Optional[str]:
            if credentials:
                return (token := credentials.credentials)
            elif access_token and self.token_type == 'access':
                return (token := access_token)
            elif refresh_token and self.token_type == 'refresh':
                return (token := refresh_token)
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail='Not authenticated',
                headers={'WWW-Authenticate': 'Bearer'}
            )
    
    
    access_token_required = TokenRequired(token_type='access')
    refresh_token_required = TokenRequired(token_type='refresh')
    
    
    class GetUser:
        def __init__(self, token: str, audience: str) -> None:
            self.token = token
            self.audience = audience
    
        async def decode(self) -> None:
            try:
                payload = jwt.decode(
                    self.token,
                    public_key,
                    algorithms=[settings.JWT_TOKEN_ALGORITHM],
                    audience=self.audience
                )
                token_data = schemas.TokenPayload(**payload)
            except (JWTError, ValidationError):
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail='Could not validate credentials'
                )
    
            jti_status = await redis.get(str(token_data.jti))
            if jti_status and jti_status == 'true':
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail='Token is invalidated'
                )
    
            self.token_data = token_data
    
        async def get_current_user(self) -> models.User:
            await self.decode()
            user = await crud.user.read_by_id(self.token_data.sub)
            if not user:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail='User not found'
                )
            return user
  • 相关阅读:
    SQL入门经典(九) 之自定义函数
    SQL入门经典(八) 之存储过程
    SQL入门经典(七) 之脚本和批处理
    SQL入门经典(六) 之视图
    超越阿里云,华为云网络服务竟然这么厉害!
    shanchushanchu
    2020-2022年中国云计算市场预测
    如何使用egit将本地代码提交到托管平台
    一个项目经理对主流项目管理工具的对比:禅道VS华为软件开发云
    从SVN到Git最强指南
  • 原文地址:https://www.cnblogs.com/lightsong/p/16879203.html
Copyright © 2020-2023  润新知