Files
RuoYi-Vue3-FastAPI/ruoyi-fastapi-backend/module_identity/controller/oidc_key_controller.py
T
insistence a89aec54aa feat: 新增 OAuth 2.0 / OIDC 统一认证中心 (#129)
* feat: 新增统一认证中心

* perf: 优化代码

* fix: 修复问题

* perf: 优化页面

* fix: 修复部分接口缺失Request参数的问题

* perf: 优化访问控制机制

* perf: 优化代码

* fix: 修复 Logout Token 缺少 exp

* perf&fix: 优化和修复

* perf: 优化代码

* perf: 优化代码

* fix: 修复测试问题
2026-10-01 22:56:05 +08:00

145 lines
6.1 KiB
Python

from typing import Annotated, Literal
from fastapi import Path, Query, Request, Response
from sqlalchemy.ext.asyncio import AsyncSession
from common.annotation.log_annotation import Log
from common.annotation.rate_limit_annotation import ApiRateLimit, ApiRateLimitPreset
from common.aspect.db_session import DBSessionDependency
from common.aspect.interface_auth import UserInterfaceAuthDependency
from common.aspect.pre_auth import CurrentUserDependency, PreAuthDependency
from common.constant import ApiNamespace
from common.enums import BusinessType
from common.router import APIRouterPro
from common.vo import DataResponseModel, PageResponseModel, ResponseBaseModel
from exceptions.exception import ServiceException
from module_admin.entity.vo.user_vo import CurrentUserModel
from module_identity.entity.vo.oidc_key_vo import OidcKeyRotateModel, OidcKeyViewModel
from module_identity.service.key_service import OidcKeyManagementService
from module_identity.service.runtime_service import OidcRuntimeService
from utils.oidc_util import OidcUtil
from utils.response_util import ResponseUtil
oidc_key_controller = APIRouterPro(
prefix='/system/oauth/key', order_num=24, tags=['系统管理-OIDC 签名密钥'], dependencies=[PreAuthDependency()]
)
def _actor(current_user: CurrentUserModel) -> str:
"""
提取管理操作者标识
:param current_user: 当前登录用户
:return: 安全截断后的用户名
:raises ServiceException: 当前用户不可用
"""
try:
return OidcUtil.actor_name(
getattr(getattr(current_user, 'user', None), 'user_name', None), error_message='当前操作者不可用'
)
except ValueError as exc:
raise ServiceException(message=str(exc)) from exc
@oidc_key_controller.get(
'/list',
summary='获取 OIDC 签名密钥分页列表接口',
description='用于获取 OIDC 签名密钥分页列表',
response_model=PageResponseModel[OidcKeyViewModel],
dependencies=[UserInterfaceAuthDependency('system:oauthKey:list')],
)
async def list_oidc_keys(
query_db: Annotated[AsyncSession, DBSessionDependency()],
status: Annotated[Literal['pending', 'active', 'retiring', 'retired', 'compromised'] | None, Query()] = None,
page_num: Annotated[int, Query(ge=1)] = 1,
page_size: Annotated[int, Query(ge=1, le=200)] = 10,
) -> Response:
rows, total = await OidcKeyManagementService.list_page(query_db, status, page_num, page_size)
readiness = await OidcRuntimeService.inspect_readiness(query_db)
return ResponseUtil.success(rows=rows, dict_content={'total': total, **readiness.as_dict()})
@oidc_key_controller.post(
'/rotate',
summary='轮换 OIDC 签名密钥接口',
description='用于轮换 OIDC 签名密钥',
response_model=DataResponseModel[OidcKeyViewModel],
dependencies=[UserInterfaceAuthDependency('system:oauthKey:rotate')],
)
@Log(title='OIDC 签名密钥', business_type=BusinessType.INSERT)
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_OAUTH_KEY_ROTATE, preset=ApiRateLimitPreset.USER_SECURITY_MUTATION)
async def rotate_oidc_key(
request: Request,
payload: OidcKeyRotateModel,
query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
) -> Response:
result = await OidcKeyManagementService.rotate(query_db, payload, _actor(current_user))
return ResponseUtil.success(msg='OIDC 签名密钥创建成功', data=result)
@oidc_key_controller.put(
'/{kid}/activate',
summary='激活 OIDC 签名密钥接口',
description='用于激活 OIDC 签名密钥',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:oauthKey:activate')],
)
@Log(title='OIDC 签名密钥', business_type=BusinessType.UPDATE)
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_OAUTH_KEY_ACTIVATE, preset=ApiRateLimitPreset.USER_SECURITY_MUTATION)
async def activate_oidc_key(
request: Request,
kid: Annotated[str, Path(min_length=1, max_length=100)],
query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
) -> Response:
result = await OidcKeyManagementService.activate(
query_db, kid, _actor(current_user), getattr(request.app.state, 'redis', None)
)
await OidcRuntimeService.refresh_readiness(request.app, query_db)
return ResponseUtil.success(msg='OIDC 签名密钥已激活', data={'changed': result})
@oidc_key_controller.put(
'/{kid}/retire',
summary='退役 OIDC 签名密钥接口',
description='用于退役 OIDC 签名密钥',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:oauthKey:retire')],
)
@Log(title='OIDC 签名密钥', business_type=BusinessType.UPDATE)
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_OAUTH_KEY_RETIRE, preset=ApiRateLimitPreset.USER_SECURITY_MUTATION)
async def retire_oidc_key(
request: Request,
kid: Annotated[str, Path(min_length=1, max_length=100)],
query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
) -> Response:
result = await OidcKeyManagementService.retire(query_db, kid, _actor(current_user))
return ResponseUtil.success(msg='OIDC 签名密钥已退役', data={'changed': result})
@oidc_key_controller.delete(
'/{kid}',
summary='删除 OIDC 签名密钥接口',
description='用于删除 OIDC 签名密钥',
response_model=ResponseBaseModel,
dependencies=[UserInterfaceAuthDependency('system:oauthKey:retire')],
)
@Log(title='OIDC 签名密钥', business_type=BusinessType.DELETE)
@ApiRateLimit(namespace=ApiNamespace.SYSTEM_OAUTH_KEY_DELETE, preset=ApiRateLimitPreset.USER_DESTRUCTIVE_MUTATION)
async def delete_oidc_key(
request: Request,
kid: Annotated[str, Path(min_length=1, max_length=100)],
query_db: Annotated[AsyncSession, DBSessionDependency()],
current_user: Annotated[CurrentUserModel, CurrentUserDependency()],
) -> Response:
result = await OidcKeyManagementService.delete(query_db, kid, _actor(current_user))
return ResponseUtil.success(msg='OIDC 签名密钥已删除', data={'changed': result})