- 后端:钉钉 OAuth 认证服务 - 后端:系统设置 API(钉钉配置) - 前端:登录页钉钉扫码入口 - 前端:系统设置页面 - 数据库迁移脚本
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
"""
|
||||
认证 API
|
||||
"""
|
||||
from fastapi import APIRouter, Depends, status, Request
|
||||
from fastapi import APIRouter, Depends, status, Request, Query
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.deps import get_current_active_user, get_db
|
||||
from app.core.logger import logger
|
||||
from app.models.user import User
|
||||
from app.schemas.auth import LoginRequest, RefreshTokenRequest, Token
|
||||
from app.schemas.auth import LoginRequest, RefreshTokenRequest, Token, DingtalkLoginRequest
|
||||
from app.schemas.base import ResponseModel
|
||||
from app.schemas.user import User as UserSchema
|
||||
from app.services.auth_service import AuthService
|
||||
from app.services.dingtalk_auth_service import DingtalkAuthService
|
||||
from app.services.system_log_service import system_log_service
|
||||
from app.schemas.system_log import SystemLogCreate
|
||||
from app.core.exceptions import UnauthorizedError
|
||||
@@ -154,3 +155,102 @@ async def verify_token(
|
||||
"user": UserSchema.model_validate(current_user).model_dump(),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# ============================================
|
||||
# 钉钉免密登录 API
|
||||
# ============================================
|
||||
|
||||
@router.post("/dingtalk/login", response_model=ResponseModel)
|
||||
async def dingtalk_login(
|
||||
login_data: DingtalkLoginRequest,
|
||||
request: Request,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> ResponseModel:
|
||||
"""
|
||||
钉钉免密登录
|
||||
|
||||
通过钉钉免登授权码登录系统
|
||||
"""
|
||||
dingtalk_service = DingtalkAuthService(db)
|
||||
|
||||
try:
|
||||
user, token = await dingtalk_service.dingtalk_login(
|
||||
tenant_id=login_data.tenant_id,
|
||||
code=login_data.code,
|
||||
)
|
||||
|
||||
# 记录登录成功日志
|
||||
await system_log_service.create_log(
|
||||
db,
|
||||
SystemLogCreate(
|
||||
level="INFO",
|
||||
type="security",
|
||||
message=f"用户 {user.username} 通过钉钉免密登录成功",
|
||||
user_id=user.id,
|
||||
user=user.username,
|
||||
ip=request.client.host if request.client else None,
|
||||
path="/api/v1/auth/dingtalk/login",
|
||||
method="POST",
|
||||
user_agent=request.headers.get("user-agent")
|
||||
)
|
||||
)
|
||||
|
||||
return ResponseModel(
|
||||
message="钉钉登录成功",
|
||||
data={
|
||||
"user": UserSchema.model_validate(user).model_dump(),
|
||||
"token": token.model_dump(),
|
||||
},
|
||||
)
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
logger.warning("dingtalk_login_failed", error=error_msg)
|
||||
|
||||
# 记录登录失败日志
|
||||
await system_log_service.create_log(
|
||||
db,
|
||||
SystemLogCreate(
|
||||
level="WARNING",
|
||||
type="security",
|
||||
message=f"钉钉免密登录失败:{error_msg}",
|
||||
ip=request.client.host if request.client else None,
|
||||
path="/api/v1/auth/dingtalk/login",
|
||||
method="POST",
|
||||
user_agent=request.headers.get("user-agent")
|
||||
)
|
||||
)
|
||||
|
||||
return ResponseModel(
|
||||
code=400,
|
||||
message=error_msg,
|
||||
data=None,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/dingtalk/config", response_model=ResponseModel)
|
||||
async def get_dingtalk_config(
|
||||
tenant_id: int = Query(default=1, description="租户ID"),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> ResponseModel:
|
||||
"""
|
||||
获取钉钉公开配置
|
||||
|
||||
前端需要使用 corpId 和 agentId 初始化钉钉JSDK
|
||||
仅返回非敏感信息
|
||||
"""
|
||||
dingtalk_service = DingtalkAuthService(db)
|
||||
|
||||
try:
|
||||
config = await dingtalk_service.get_public_config(tenant_id)
|
||||
return ResponseModel(
|
||||
message="获取成功",
|
||||
data=config,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("get_dingtalk_config_failed", error=str(e))
|
||||
return ResponseModel(
|
||||
code=500,
|
||||
message="获取钉钉配置失败",
|
||||
data={"enabled": False, "corp_id": None, "agent_id": None},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user