"""认证 API""" import base64 import json from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy.orm import Session from app.core.database import get_db from app.core.casdoor import casdoor_sdk from app.core.security import create_access_token from app.core.config import settings from app.models.user import User from app.schemas.auth import Token, UserInfo from datetime import datetime router = APIRouter(prefix="/api/auth", tags=["认证"]) def decode_jwt_payload(token: str) -> dict: """直接解码 JWT payload,不验签(Casdoor 已完成认证)""" payload_b64 = token.split(".")[1] rem = len(payload_b64) % 4 if rem: payload_b64 += "=" * (4 - rem) return json.loads(base64.urlsafe_b64decode(payload_b64)) @router.get("/login") def login(): """获取 Casdoor 登录 URL""" return {"url": casdoor_sdk.get_auth_link(settings.CASDOOR_REDIRECT_URL)} class CallbackRequest(BaseModel): code: str state: str @router.post("/callback", response_model=Token) def callback(body: CallbackRequest, db: Session = Depends(get_db)): """Casdoor 登录回调""" try: token_response = casdoor_sdk.get_oauth_token(code=body.code) if isinstance(token_response, dict) and "error" in token_response: raise HTTPException(status_code=400, detail=token_response.get("error_description", token_response["error"])) access_token = token_response.get("access_token") if isinstance(token_response, dict) else token_response if not access_token: raise HTTPException(status_code=400, detail="Casdoor 未返回 access_token") casdoor_user = decode_jwt_payload(access_token) user = db.query(User).filter(User.casdoor_id == casdoor_user["sub"]).first() if not user: user = User( casdoor_id=casdoor_user["sub"], username=casdoor_user.get("name") or casdoor_user.get("preferred_username", ""), email=casdoor_user.get("email"), role="user" ) db.add(user) user.last_login = datetime.utcnow() db.commit() jwt_token = create_access_token({"sub": str(user.id), "username": user.username}) return {"access_token": jwt_token} except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/profile", response_model=UserInfo) def get_profile(token: str, db: Session = Depends(get_db)): """获取当前用户信息""" from app.core.security import verify_token payload = verify_token(token) if not payload: raise HTTPException(status_code=401, detail="无效的令牌") user = db.query(User).filter(User.id == int(payload["sub"])).first() if not user: raise HTTPException(status_code=404, detail="用户不存在") return user