- 配置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
223 lines
8.2 KiB
C#
223 lines
8.2 KiB
C#
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 });
|
||
}
|
||
}
|
||
}
|