gpt4 book ai didi

c# - 根据 roles/api_key 生成 Swashbuckle API 文档

转载 作者:行者123 更新时间:2023-11-30 21:47:53 26 4
gpt4 key购买 nike

我正在使用 Swashbuckle (5.3.2),它生成了一个很好的 API 文档。

为了澄清我的问题,我设置了一个没有实际意义的小示例项目。

API 只能与有效的 API key 一起使用。为此,我引入了一个 ApiKeyFilter 来验证 api_key 并读出相应的角色。

ApiKeyFilter

public class ApiKeyFilter : IAuthenticationFilter
{
private static Dictionary<string, String[]> allowedApps = new Dictionary<string, String[]>();
private readonly string authenticationScheme = "Bearer";
private readonly string queryStringApiKey = "api_key";

public bool AllowMultiple
{
get { return false; }
}

public ApiKeyFilter()
{
if (allowedApps.Count == 0)
{
allowedApps.Add("PetLover_api_key", new []{"PetLover"});
allowedApps.Add("CarOwner_api_key", new []{"CarOwner"});
allowedApps.Add("Admin_api_key", new []{"PetLover","CarOwner"});
}
}

public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
var req = context.Request;
Dictionary<string, string> queryStrings = req.GetQueryNameValuePairs().ToDictionary(x => x.Key.ToLower(), x => x.Value);
string rawAuthzHeader = null;
if (queryStrings.ContainsKey(queryStringApiKey))
{
rawAuthzHeader = queryStrings[queryStringApiKey];
}
else if (req.Headers.Authorization != null && authenticationScheme.Equals(req.Headers.Authorization.Scheme, StringComparison.OrdinalIgnoreCase))
{
rawAuthzHeader = req.Headers.Authorization.Parameter;
}
if (rawAuthzHeader != null && allowedApps.ContainsKey(rawAuthzHeader))
{
var currentPrincipal = new GenericPrincipal(new GenericIdentity(rawAuthzHeader), allowedApps[rawAuthzHeader]);
context.Principal = currentPrincipal;
}
else
{
context.ErrorResult = new UnauthorizedResult(new AuthenticationHeaderValue[0], context.Request);
}

return Task.FromResult(0);
}

public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
context.Result = new ResultWithChallenge(context.Result);
return Task.FromResult(0);
}


}

public class ResultWithChallenge : IHttpActionResult
{
private readonly string authenticationScheme = "amx";
private readonly IHttpActionResult next;

public ResultWithChallenge(IHttpActionResult next)
{
this.next = next;
}

public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = await next.ExecuteAsync(cancellationToken);

if (response.StatusCode == HttpStatusCode.Unauthorized)
{
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(authenticationScheme));
}

return response;
}
}

Controller /资源只有在请求者具有相应的角色时才能被访问。

宠物 Controller

[Authorize(Roles = "PetLover")]
[RoutePrefix("api/pets")]
public class PetController : ApiController
{
// GET api/pet
[Route]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}

// GET api/pet/5
[Route("{id:int}")]
public string Get(int id)
{
return "value";
}

// POST api/pet
[Route]
public void Post([FromBody]string value)
{
}

// PUT api/pet/5
public void Put(int id, [FromBody]string value)
{
}

// DELETE api/pet/5
public void Delete(int id)
{
}
}

汽车 Controller

[RoutePrefix("api/cars")]
public class CarController : ApiController
{
// GET api/car
[AllowAnonymous]
[Route]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}

// GET api/car/5
[Authorize(Roles = "CarOwner")]
[Route("{id:int}")]
public string Get(int id)
{
return "value";
}

// POST api/car
[Authorize(Roles = "CarOwner")]
[Route]
public void Post([FromBody]string value)
{
}
}


WebApiConfig

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();

config.Filters.Add(new ApiKeyFilter());

//config.MessageHandlers.Add(new CustomAuthenticationMessageHandler());
}
}

到目前为止一切顺利。这里没问题。

问题:


现在我希望在 API 生成期间考虑“用户”角色。我只想在文档中显示用户可以使用此 api_key 使用的资源和操作。

输出应该看起来像 (/swagger/ui/index?api_key=XXX):

  1. Admin_api_key:

    • 汽车
      • 获取/api/汽车
      • 发布/api/cars
      • 获取/api/cars/{id}
    • 宠物
      • 获取/api/宠物
      • 发布/api/pets
      • 获取/api/pets/{id}
  2. CarOwner_api_key:

    • 汽车
      • 获取/api/汽车
      • 发布/api/cars
      • 获取/api/cars/{id}
  3. PetLover_api_key:

    • 汽车
      • 获取/api/汽车
    • 宠物
      • 获取/api/宠物
      • 发布/api/pets
      • 获取/api/pets/{id}
  4. 无效的 api_key:

    • 没有可显示的内容

在 API 规范生成期间,我无权访问 HttpRequest 以读取任何查询字符串或任何 header 信息。

我已经查看了 DelegatingHandler 但我无法在任何 Swashbuckle 过滤器(OperationFilter、DocumentFilter)中读出 Principal 而且我也无法在 CustomProvider 中读出 Principal

public class CustomAuthenticationMessageHandler : DelegatingHandler
{
private static Dictionary<string, String[]> allowedApps = new Dictionary<string, String[]>();
private readonly string authenticationScheme = "Bearer";
private readonly string queryStringApiKey = "api_key";

public bool AllowMultiple
{
get { return false; }
}

public CustomAuthenticationMessageHandler()
{
if (allowedApps.Count == 0)
{
allowedApps.Add("PetLover_api_key", new[] {"PetLover"});
allowedApps.Add("CarOwner_api_key", new[] {"CarOwner"});
allowedApps.Add("Admin_api_key", new[] {"PetLover", "CarOwner"});
}
}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var req = request;
Dictionary<string, string> queryStrings = req.GetQueryNameValuePairs().ToDictionary(x => x.Key.ToLower(), x => x.Value);
string rawAuthzHeader = null;
if (queryStrings.ContainsKey(queryStringApiKey))
{
rawAuthzHeader = queryStrings[queryStringApiKey];
}
else if (req.Headers.Authorization != null && authenticationScheme.Equals(req.Headers.Authorization.Scheme, StringComparison.OrdinalIgnoreCase))
{
rawAuthzHeader = req.Headers.Authorization.Parameter;
}
if (rawAuthzHeader != null && allowedApps.ContainsKey(rawAuthzHeader))
{
var currentPrincipal = new GenericPrincipal(new GenericIdentity(rawAuthzHeader), allowedApps[rawAuthzHeader]);
request.GetRequestContext().Principal = currentPrincipal;
}
else
{

}

return await base.SendAsync(request, cancellationToken);
}
}



我发现了一些类似的问题,但没有真正的答案。
Web API Documentation using swagger
Restrict access to certain API controllers in Swagger using Swashbuckle and ASP.NET Identity
{hxxps://}github.com/domaindrivendev/Swashbuckle/issues/334
{hxxps://}github.com/domaindrivendev/Swashbuckle/issues/735
{hxxps://}github.com/domaindrivendev/Swashbuckle/issues/478

最佳答案

我现在找到了适合我的解决方案。

我使用 CustomAuthenticationMessageHandler(与我的问题相同)将规则放入 HttpContext。

public class CustomAuthenticationMessageHandler : DelegatingHandler
{
private static Dictionary<string, String[]> allowedApps = new Dictionary<string, String[]>();
private readonly string authenticationScheme = "Bearer";
private readonly string queryStringApiKey = "api_key";

public bool AllowMultiple
{
get { return false; }
}

public CustomAuthenticationMessageHandler()
{
if (allowedApps.Count == 0)
{
allowedApps.Add("PetLover_api_key", new[] {"PetLover"});
allowedApps.Add("CarOwner_api_key", new[] {"CarOwner"});
allowedApps.Add("Admin_api_key", new[] {"PetLover", "CarOwner"});
}
}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var req = request;
Dictionary<string, string> queryStrings = req.GetQueryNameValuePairs().ToDictionary(x => x.Key.ToLower(), x => x.Value);
string rawAuthzHeader = null;
if (queryStrings.ContainsKey(queryStringApiKey))
{
rawAuthzHeader = queryStrings[queryStringApiKey];
}
else if (req.Headers.Authorization != null && authenticationScheme.Equals(req.Headers.Authorization.Scheme, StringComparison.OrdinalIgnoreCase))
{
rawAuthzHeader = req.Headers.Authorization.Parameter;
}
if (rawAuthzHeader != null && allowedApps.ContainsKey(rawAuthzHeader))
{
var currentPrincipal = new GenericPrincipal(new GenericIdentity(rawAuthzHeader), allowedApps[rawAuthzHeader]);
request.GetRequestContext().Principal = currentPrincipal;
}
else
{

}

return await base.SendAsync(request, cancellationToken);
}
}

我引入了一个自定义的 Swashbuckle IDocumentFilter,它从 HttpContext 中读取规则,并从 SwaggerDocument 中删除不是的操作和资源此规则允许(基于 api_key)。

public class AuthorizeRoleFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
IPrincipal user = HttpContext.Current.User;

foreach (ApiDescription apiDescription in apiExplorer.ApiDescriptions)
{
var authorizeAttributes = apiDescription
.ActionDescriptor.GetCustomAttributes<AuthorizeAttribute>().ToList();
authorizeAttributes.AddRange(apiDescription
.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AuthorizeAttribute>());

if (!authorizeAttributes.Any())
continue;

var roles =
authorizeAttributes
.SelectMany(attr => attr.Roles.Split(','))
.Distinct()
.ToList();
if (!user.Identity.IsAuthenticated || !roles.Any(role => user.IsInRole(role) || role == ""))
{
string key = "/" + apiDescription.RelativePath;
PathItem pathItem = swaggerDoc.paths[key];
switch (apiDescription.HttpMethod.Method.ToLower())
{
case "get":
pathItem.get = null;
break;
case "put":
pathItem.put = null;
break;
case "post":
pathItem.post = null;
break;
case "delete":
pathItem.delete = null;
break;
case "options":
pathItem.options = null;
break;
case "head":
pathItem.head = null;
break;
case "patch":
pathItem.patch = null;
break;
}
if (pathItem.get == null &&
pathItem.put == null &&
pathItem.post == null &&
pathItem.delete == null &&
pathItem.options == null &&
pathItem.head == null &&
pathItem.patch == null)
{
swaggerDoc.paths.Remove(key);
}
}
}

swaggerDoc.paths = swaggerDoc.paths.Count == 0 ? null : swaggerDoc.paths;
swaggerDoc.definitions = swaggerDoc.paths == null ? null : swaggerDoc.definitions;
}
}

我的 WebApiConfig 现在看起来像这样。我删除了我的 ApiKeyFilter,因为不再需要它。

WebApiConfig

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services

// Web API routes
config.MapHttpAttributeRoutes();

//config.Filters.Add(new ApiKeyFilter());

config.MessageHandlers.Add(new CustomAuthenticationMessageHandler());
}
}

SwaggerConfig

public class SwaggerConfig
{
public static void Register()
{
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "SwashbuckleExample");
c.DocumentFilter<AuthorizeRoleFilter>();
})
.EnableSwaggerUi(c => { });
}
}



附加

在我的项目中,我使用了一个 CustomProvider,它根据 api_key 缓存 SwaggerDocument

 public class CachingSwaggerProvider : ISwaggerProvider
{
private static ConcurrentDictionary<string, SwaggerDocument> _cache =
new ConcurrentDictionary<string, SwaggerDocument>();

private readonly ISwaggerProvider _swaggerProvider;

public CachingSwaggerProvider(ISwaggerProvider swaggerProvider)
{
_swaggerProvider = swaggerProvider;
}

public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
{
HttpContext httpContext = HttpContext.Current;
string name = httpContext.User.Identity.Name;
var cacheKey = string.Format("{0}_{1}_{2}", rootUrl, apiVersion, name);
return _cache.GetOrAdd(cacheKey, (key) => _swaggerProvider.GetSwagger(rootUrl, apiVersion));
}
}

关于c# - 根据 roles/api_key 生成 Swashbuckle API 文档,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38070950/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com