Funcionalidades incluídas:
fastapi_jwt/
├── main.py
├── database.py
├── models.py
├── schemas.py
├── auth.py
├── create_db.py
├── requirements.txt
└── test.db # criado automaticamente
pip install fastapi uvicorn sqlalchemy pydantic python-jose[cryptography] passlib[bcrypt]
database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
auth.py
— geração e verificação de tokenfrom datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.orm import Session
from models import User
from database import get_db
SECRET_KEY = "secret-jwt-key" # Troque isso em produção
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
def hash_password(password: str):
return pwd_context.hash(password)
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def create_access_token(data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15))
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Credenciais inválidas",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
user_id: int = payload.get("sub")
if user_id is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = db.query(User).filter(User.id == user_id).first()
if user is None:
raise credentials_exception
return user
models.py
from sqlalchemy import Column, Integer, String
from database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
email = Column(String, unique=True, index=True)
password = Column(String)
schemas.py
from pydantic import BaseModel, EmailStr
class UserCreate(BaseModel):
name: str
email: EmailStr
password: str
class UserLogin(BaseModel):
email: EmailStr
password: str
class UserResponse(BaseModel):
id: int
name: str
email: EmailStr
class Config:
from_attributes = True
class Token(BaseModel):
access_token: str
token_type: str
create_db.py
from database import Base, engine
from models import User
Base.metadata.create_all(bind=engine)
main.py
from fastapi import FastAPI, Depends, HTTPException, status
from sqlalchemy.orm import Session
from fastapi.security import OAuth2PasswordRequestForm
from database import get_db
from models import User
from schemas import UserCreate, UserResponse, UserLogin, Token
from auth import hash_password, verify_password, create_access_token, get_current_user
app = FastAPI(title="API com JWT + SQLite", version="1.0.0")
@app.post("/register", response_model=UserResponse)
def register(user: UserCreate, db: Session = Depends(get_db)):
existing = db.query(User).filter(User.email == user.email).first()
if existing:
raise HTTPException(status_code=400, detail="E-mail já cadastrado")
hashed = hash_password(user.password)
db_user = User(name=user.name, email=user.email, password=hashed)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
@app.post("/login", response_model=Token)
def login(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
user = db.query(User).filter(User.email == form_data.username).first()
if not user or not verify_password(form_data.password, user.password):
raise HTTPException(status_code=401, detail="Credenciais inválidas")
token = create_access_token(data={"sub": str(user.id)})
return {"access_token": token, "token_type": "bearer"}
@app.get("/me", response_model=UserResponse)
def read_current_user(current_user: User = Depends(get_current_user)):
return current_user
python create_db.py
uvicorn main:app --reload
POST /register
— cria um usuárioPOST /login
— retorna um token JWTGET /me
— retorna dados do usuário logado (token obrigatório)/me
:/login
e envie e-mail + senha.access_token
da resposta.Bearer <token>
/me
— agora está autenticado!Exemplo real: o botão “Entrar com Google” ou “Entrar com GitHub” usa OAuth2 por trás.
<HEADER>.<PAYLOAD>.<SIGNATURE>
O JWT é frequentemente usado como formato do token dentro do fluxo OAuth2.
Característica | OAuth2 | JWT | |
É um protocolo? | ✅ Sim | ❌ Não | |
É um formato de token? | ❌ Não | ✅ Sim | |
Garante controle de acesso? | ✅ Sim | ✅ Indiretamente | |
Requer servidor de autenticação? | ✅ Sim | ❌ (é só o formato do token) | |
É usado com FastAPI? | ✅ Sim | ✅ Sim |
from fastapi.security import OAuth2PasswordBearer
from jose import jwt
jwt.encode({...}, SECRET_KEY, algorithm="HS256")
Termo | O que é | Exemplo de uso | |
OAuth2 | Protocolo de acesso | Login com senha, token de autorização | |
JWT | Formato de token | Transporte seguro de dados autenticados |