This commit is contained in:
2026-03-04 01:08:00 +08:00
parent 006a30ea0c
commit 24c799f08e
6 changed files with 960 additions and 214 deletions
+426 -69
View File
@@ -1,40 +1,33 @@
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.ext.asyncio import AsyncSession
from pydantic import BaseModel, EmailStr
from typing import Optional
from pydantic import BaseModel
from typing import Optional, List
from datetime import timedelta
from sqlalchemy import select
from database.database import get_db_session
from services.auth_service import (
authenticate_user,
create_access_token,
create_user,
get_user_by_username,
get_user_by_email,
get_current_active_user,
get_current_admin_user
get_password_hash
)
from models.database import User
from models.database import User, Role, Permission, UserRole, RolePermission
from config.settings import settings
from utils.logger import get_logger
logger = get_logger(__name__)
router = APIRouter(prefix="/api/auth", tags=["认证"])
class UserCreate(BaseModel):
username: str
email: EmailStr
password: str
full_name: Optional[str] = None
class UserResponse(BaseModel):
id: int
username: str
email: str
full_name: Optional[str]
is_active: bool
is_superuser: bool
roles: List[str]
class Config:
from_attributes = True
@@ -51,6 +44,63 @@ class LoginRequest(BaseModel):
password: str
class RoleCreate(BaseModel):
code: str
name: str
description: Optional[str] = None
class RoleResponse(BaseModel):
id: int
code: str
name: str
description: Optional[str]
is_system: bool
permissions: List[str]
class Config:
from_attributes = True
class PermissionCreate(BaseModel):
code: str
name: str
module: Optional[str] = None
description: Optional[str] = None
class PermissionResponse(BaseModel):
id: int
code: str
name: str
module: Optional[str]
description: Optional[str]
class Config:
from_attributes = True
class UserCreate(BaseModel):
username: str
email: str
password: str
full_name: Optional[str] = None
role_ids: List[int] = []
class UserUpdate(BaseModel):
email: Optional[str] = None
full_name: Optional[str] = None
is_active: Optional[bool] = None
role_ids: Optional[List[int]] = None
def check_admin(user: User) -> bool:
if not user.is_superuser:
raise HTTPException(status_code=403, detail="需要管理员权限")
return True
@router.post("/login", response_model=Token)
async def login(
form_data: OAuth2PasswordRequestForm = Depends(),
@@ -72,7 +122,14 @@ async def login(
return Token(
access_token=access_token,
token_type="bearer",
user=UserResponse.from_orm(user)
user=UserResponse(
id=user.id,
username=user.username,
email=user.email,
full_name=user.full_name,
is_active=user.is_active,
roles=[r.code for r in user.roles]
)
)
@@ -96,39 +153,29 @@ async def login_json(
return Token(
access_token=access_token,
token_type="bearer",
user=UserResponse.from_orm(user)
user=UserResponse(
id=user.id,
username=user.username,
email=user.email,
full_name=user.full_name,
is_active=user.is_active,
roles=[r.code for r in user.roles]
)
)
@router.post("/register", response_model=UserResponse, status_code=201)
async def register(
user_data: UserCreate,
db_session: AsyncSession = Depends(get_db_session)
):
existing_user = await get_user_by_username(db_session, user_data.username)
if existing_user:
raise HTTPException(status_code=400, detail="用户名已存在")
existing_email = await get_user_by_email(db_session, user_data.email)
if existing_email:
raise HTTPException(status_code=400, detail="邮箱已被注册")
user = await create_user(
db_session=db_session,
username=user_data.username,
email=user_data.email,
password=user_data.password,
full_name=user_data.full_name
)
return UserResponse.from_orm(user)
@router.get("/me", response_model=UserResponse)
async def get_current_user_info(
current_user: User = Depends(get_current_active_user)
):
return UserResponse.from_orm(current_user)
return UserResponse(
id=current_user.id,
username=current_user.username,
email=current_user.email,
full_name=current_user.full_name,
is_active=current_user.is_active,
roles=[r.code for r in current_user.roles]
)
@router.post("/logout")
@@ -136,58 +183,368 @@ async def logout():
return {"message": "已登出"}
@router.get("/users", response_model=list[UserResponse])
@router.get("/users", response_model=List[UserResponse])
async def list_users(
db_session: AsyncSession = Depends(get_db_session),
admin_user: User = Depends(get_current_admin_user)
current_user: User = Depends(get_current_active_user)
):
from sqlalchemy import select
result = await db_session.execute(select(User))
check_admin(current_user)
result = await db_session.execute(
select(User).options(
select(User).options()
)
)
users = result.scalars().all()
return [UserResponse.from_orm(u) for u in users]
return [
UserResponse(
id=u.id,
username=u.username,
email=u.email,
full_name=u.full_name,
is_active=u.is_active,
roles=[r.code for r in u.roles]
) for u in users
]
@router.put("/users/{user_id}/toggle-active", response_model=UserResponse)
async def toggle_user_active(
user_id: int,
@router.post("/users", response_model=UserResponse, status_code=201)
async def create_user(
user_data: UserCreate,
db_session: AsyncSession = Depends(get_db_session),
admin_user: User = Depends(get_current_admin_user)
current_user: User = Depends(get_current_active_user)
):
from sqlalchemy import select
result = await db_session.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
check_admin(current_user)
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
existing = await db_session.execute(
select(User).where(User.username == user_data.username)
)
if existing.scalar_one_or_none():
raise HTTPException(status_code=400, detail="用户名已存在")
if user.id == admin_user.id:
raise HTTPException(status_code=400, detail="不能禁用自己的账户")
existing_email = await db_session.execute(
select(User).where(User.email == user_data.email)
)
if existing_email.scalar_one_or_none():
raise HTTPException(status_code=400, detail="邮箱已存在")
user = User(
username=user_data.username,
email=user_data.email,
hashed_password=get_password_hash(user_data.password),
full_name=user_data.full_name,
is_active=True
)
db_session.add(user)
await db_session.flush()
for role_id in user_data.role_ids:
user_role = UserRole(user_id=user.id, role_id=role_id)
db_session.add(user_role)
user.is_active = not user.is_active
await db_session.commit()
await db_session.refresh(user)
return UserResponse.from_orm(user)
logger.info(f"管理员 {current_user.username} 创建了用户 {user.username}")
return UserResponse(
id=user.id,
username=user.username,
email=user.email,
full_name=user.full_name,
is_active=user.is_active,
roles=[r.code for r in user.roles]
)
@router.put("/users/{user_id}/toggle-admin", response_model=UserResponse)
async def toggle_user_admin(
@router.put("/users/{user_id}", response_model=UserResponse)
async def update_user(
user_id: int,
user_data: UserUpdate,
db_session: AsyncSession = Depends(get_db_session),
admin_user: User = Depends(get_current_admin_user)
current_user: User = Depends(get_current_active_user)
):
from sqlalchemy import select
check_admin(current_user)
result = await db_session.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
if user.id == admin_user.id:
raise HTTPException(status_code=400, detail="不能修改自己的管理员权限")
if user_data.email is not None:
user.email = user_data.email
if user_data.full_name is not None:
user.full_name = user_data.full_name
if user_data.is_active is not None:
user.is_active = user_data.is_active
if user_data.role_ids is not None:
await db_session.execute(
select(UserRole).where(UserRole.user_id == user_id)
)
for ur in (await db_session.execute(select(UserRole).where(UserRole.user_id == user_id))).scalars().all():
await db_session.delete(ur)
for role_id in user_data.role_ids:
user_role = UserRole(user_id=user.id, role_id=role_id)
db_session.add(user_role)
user.is_superuser = not user.is_superuser
await db_session.commit()
await db_session.refresh(user)
return UserResponse.from_orm(user)
logger.info(f"管理员 {current_user.username} 更新了用户 {user.username}")
return UserResponse(
id=user.id,
username=user.username,
email=user.email,
full_name=user.full_name,
is_active=user.is_active,
roles=[r.code for r in user.roles]
)
@router.delete("/users/{user_id}")
async def delete_user(
user_id: int,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
check_admin(current_user)
result = await db_session.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
if user.id == current_user.id:
raise HTTPException(status_code=400, detail="不能删除自己的账户")
username = user.username
await db_session.delete(user)
await db_session.commit()
logger.info(f"管理员 {current_user.username} 删除了用户 {username}")
return {"message": "用户已删除"}
@router.put("/users/{user_id}/reset-password")
async def reset_user_password(
user_id: int,
new_password: str,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
check_admin(current_user)
result = await db_session.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
user.hashed_password = get_password_hash(new_password)
await db_session.commit()
logger.info(f"管理员 {current_user.username} 重置了用户 {user.username} 的密码")
return {"message": "密码已重置"}
@router.get("/roles", response_model=List[RoleResponse])
async def list_roles(
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
check_admin(current_user)
result = await db_session.execute(select(Role))
roles = result.scalars().all()
return [
RoleResponse(
id=r.id,
code=r.code,
name=r.name,
description=r.description,
is_system=r.is_system,
permissions=[p.code for p in r.permissions]
) for r in roles
]
@router.post("/roles", response_model=RoleResponse, status_code=201)
async def create_role(
role_data: RoleCreate,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
check_admin(current_user)
existing = await db_session.execute(
select(Role).where(Role.code == role_data.code)
)
if existing.scalar_one_or_none():
raise HTTPException(status_code=400, detail="角色编码已存在")
role = Role(
code=role_data.code,
name=role_data.name,
description=role_data.description
)
db_session.add(role)
await db_session.commit()
await db_session.refresh(role)
logger.info(f"管理员 {current_user.username} 创建了角色 {role.code}")
return RoleResponse(
id=role.id,
code=role.code,
name=role.name,
description=role.description,
is_system=role.is_system,
permissions=[]
)
@router.put("/roles/{role_id}", response_model=RoleResponse)
async def update_role(
role_id: int,
role_data: RoleCreate,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
check_admin(current_user)
result = await db_session.execute(select(Role).where(Role.id == role_id))
role = result.scalar_one_or_none()
if not role:
raise HTTPException(status_code=404, detail="角色不存在")
if role.is_system:
raise HTTPException(status_code=400, detail="系统角色不能修改")
role.name = role_data.name
role.description = role_data.description
await db_session.commit()
await db_session.refresh(role)
return RoleResponse(
id=role.id,
code=role.code,
name=role.name,
description=role.description,
is_system=role.is_system,
permissions=[p.code for p in role.permissions]
)
@router.delete("/roles/{role_id}")
async def delete_role(
role_id: int,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
check_admin(current_user)
result = await db_session.execute(select(Role).where(Role.id == role_id))
role = result.scalar_one_or_none()
if not role:
raise HTTPException(status_code=404, detail="角色不存在")
if role.is_system:
raise HTTPException(status_code=400, detail="系统角色不能删除")
await db_session.delete(role)
await db_session.commit()
logger.info(f"管理员 {current_user.username} 删除了角色 {role.code}")
return {"message": "角色已删除"}
@router.put("/roles/{role_id}/permissions")
async def set_role_permissions(
role_id: int,
permission_ids: List[int],
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
check_admin(current_user)
result = await db_session.execute(select(Role).where(Role.id == role_id))
role = result.scalar_one_or_none()
if not role:
raise HTTPException(status_code=404, detail="角色不存在")
for rp in (await db_session.execute(select(RolePermission).where(RolePermission.role_id == role_id))).scalars().all():
await db_session.delete(rp)
for perm_id in permission_ids:
rp = RolePermission(role_id=role_id, permission_id=perm_id)
db_session.add(rp)
await db_session.commit()
logger.info(f"管理员 {current_user.username} 更新了角色 {role.code} 的权限")
return {"message": "权限已更新"}
@router.get("/permissions", response_model=List[PermissionResponse])
async def list_permissions(
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
check_admin(current_user)
result = await db_session.execute(select(Permission))
permissions = result.scalars().all()
return [PermissionResponse.from_orm(p) for p in permissions]
@router.post("/permissions", response_model=PermissionResponse, status_code=201)
async def create_permission(
perm_data: PermissionCreate,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
check_admin(current_user)
existing = await db_session.execute(
select(Permission).where(Permission.code == perm_data.code)
)
if existing.scalar_one_or_none():
raise HTTPException(status_code=400, detail="权限编码已存在")
permission = Permission(
code=perm_data.code,
name=perm_data.name,
module=perm_data.module,
description=perm_data.description
)
db_session.add(permission)
await db_session.commit()
await db_session.refresh(permission)
logger.info(f"管理员 {current_user.username} 创建了权限 {permission.code}")
return PermissionResponse.from_orm(permission)
@router.delete("/permissions/{permission_id}")
async def delete_permission(
permission_id: int,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
check_admin(current_user)
result = await db_session.execute(select(Permission).where(Permission.id == permission_id))
permission = result.scalar_one_or_none()
if not permission:
raise HTTPException(status_code=404, detail="权限不存在")
await db_session.delete(permission)
await db_session.commit()
logger.info(f"管理员 {current_user.username} 删除了权限 {permission.code}")
return {"message": "权限已删除"}