fengling-console/Controllers/OAuthClientsController.cs
Sam c8cb7c06bc feat: 添加Console API认证和OpenIddict集成
- 配置AuthService使用OpenIddict reference tokens
- 添加fengling-api客户端用于introspection验证
- 配置Console API通过OpenIddict验证reference tokens
- 实现Tenant/Users/Roles/OAuthClients CRUD API
- 添加GatewayController服务注册API
- 重构Repository和Service层支持多租户

BREAKING CHANGE: API认证现在使用OpenIddict reference tokens
2026-02-08 19:01:25 +08:00

223 lines
8.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

namespace Fengling.Console.Controllers;
/// <summary>
/// OAuth客户端管理控制器
/// </summary>
[ApiController]
[Route("api/console/[controller]")]
[Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]
public class OAuthClientsController : ControllerBase
{
private readonly IOAuthClientService _service;
private readonly ILogger<OAuthClientsController> _logger;
public OAuthClientsController(
IOAuthClientService service,
ILogger<OAuthClientsController> logger)
{
_service = service;
_logger = logger;
}
/// <summary>
/// 获取OAuth客户端列表
/// </summary>
/// <param name="query">分页查询参数支持按显示名称、客户端ID和状态筛选</param>
/// <returns>分页的OAuth客户端列表包含总数量、分页信息和客户端详情</returns>
/// <response code="200">成功返回OAuth客户端分页列表</response>
/// <response code="500">服务器内部错误</response>
[HttpGet]
[Produces("application/json")]
[ProducesResponseType(typeof(OAuthClientListDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<OAuthClientListDto>> GetClients([FromQuery] OAuthClientQueryDto query)
{
try
{
var (items, totalCount) = await _service.GetClientsAsync(query.Page, query.PageSize, query.DisplayName, query.ClientId, query.Status);
var result = new OAuthClientListDto
{
Items = items.ToList(),
TotalCount = totalCount,
Page = query.Page,
PageSize = query.PageSize
};
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting clients");
return StatusCode(500, new { message = ex.Message });
}
}
/// <summary>
/// 获取OAuth客户端选项
/// </summary>
/// <returns>包含客户端类型、授权类型、授权范围等可选值的配置选项</returns>
/// <response code="200">成功返回客户端配置选项</response>
[HttpGet("options")]
[Produces("application/json")]
[ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
public ActionResult<object> GetClientOptions()
{
return Ok(_service.GetClientOptions());
}
/// <summary>
/// 获取单个OAuth客户端详情
/// </summary>
/// <param name="id">客户端唯一标识符</param>
/// <returns>OAuth客户端的完整配置信息</returns>
/// <response code="200">成功返回客户端详情</response>
/// <response code="404">客户端不存在</response>
/// <response code="500">服务器内部错误</response>
[HttpGet("{id}")]
[Produces("application/json")]
[ProducesResponseType(typeof(OAuthClientDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<OAuthClientDto>> GetClient(string id)
{
try
{
var client = await _service.GetClientAsync(id);
if (client == null)
{
return NotFound();
}
return Ok(client);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting client {Id}", id);
return StatusCode(500, new { message = ex.Message });
}
}
/// <summary>
/// 创建新的OAuth客户端
/// </summary>
/// <param name="dto">创建客户端所需的配置信息</param>
/// <returns>创建的OAuth客户端详情</returns>
/// <response code="201">成功创建客户端</response>
/// <response code="400">请求参数无效或客户端ID已存在</response>
/// <response code="500">服务器内部错误</response>
[HttpPost]
[Produces("application/json")]
[ProducesResponseType(typeof(OAuthClientDto), StatusCodes.Status201Created)]
[ProducesResponseType(typeof(object), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<OAuthClientDto>> CreateClient([FromBody] CreateClientDto dto)
{
try
{
var result = await _service.CreateClientAsync(dto);
return StatusCode(201, result);
}
catch (InvalidOperationException ex)
{
return BadRequest(new { message = ex.Message });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating client");
return StatusCode(500, new { message = ex.Message });
}
}
/// <summary>
/// 为指定客户端生成新的密钥
/// </summary>
/// <param name="id">客户端唯一标识符</param>
/// <returns>包含新生成的客户端密钥信息</returns>
/// <response code="200">成功生成新密钥</response>
/// <response code="404">客户端不存在</response>
/// <response code="500">服务器内部错误</response>
[HttpPost("{id}/generate-secret")]
[Produces("application/json")]
[ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult> GenerateSecret(string id)
{
try
{
var result = await _service.GenerateSecretAsync(id);
return Ok(result);
}
catch (KeyNotFoundException ex)
{
return NotFound(new { message = ex.Message });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error generating secret for client {Id}", id);
return StatusCode(500, new { message = ex.Message });
}
}
/// <summary>
/// 删除指定的OAuth客户端
/// </summary>
/// <param name="id">客户端唯一标识符</param>
/// <returns>无内容响应</returns>
/// <response code="204">成功删除客户端</response>
/// <response code="404">客户端不存在</response>
/// <response code="500">服务器内部错误</response>
[HttpDelete("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> DeleteClient(string id)
{
try
{
await _service.DeleteClientAsync(id);
return NoContent();
}
catch (KeyNotFoundException ex)
{
return NotFound(new { message = ex.Message });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error deleting client {Id}", id);
return StatusCode(500, new { message = ex.Message });
}
}
/// <summary>
/// 更新指定的OAuth客户端
/// </summary>
/// <param name="id">客户端唯一标识符</param>
/// <param name="dto">需要更新的客户端配置信息</param>
/// <returns>无内容响应</returns>
/// <response code="204">成功更新客户端</response>
/// <response code="404">客户端不存在</response>
/// <response code="500">服务器内部错误</response>
[HttpPut("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> UpdateClient(string id, [FromBody] UpdateClientDto dto)
{
try
{
await _service.UpdateClientAsync(id, dto);
return NoContent();
}
catch (KeyNotFoundException ex)
{
return NotFound(new { message = ex.Message });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error updating client {Id}", id);
return StatusCode(500, new { message = ex.Message });
}
}
}