前言
當需要在生產環境中提供 Swagger UI 時,我們可以通過身份驗證,控制只有授權用戶才能訪問 Swagger UI 頁面。
但是我們希望更進一步,每個用戶只能看到授權給他的終結點,而不會暴露其他未授權終結點信息。
比如, API 提供了方法 A 和 方法 B,而對于用戶 zhangsan 來說,他在 Swagger UI 頁面只能看到方法 A 的說明,而不會知道方法 B 的存在。
思路
Swagger UI 頁面展示的數據來源,其實是/swagger/v1/swagger.json
文件:
app.UseSwaggerUI(c?=>?c.SwaggerEndpoint("/swagger/v1/swagger.json",?"WebApplication1?v1"));
而swagger.json
的格式是遵循 OpenAPI 規范的。
其中,paths
定義了 API 的所有終結點:
那么,只需要保證swagger.json
包含的paths
僅定義了授權接口的終結點即可。
雖然我們不能控制swagger.json
文件的生成,但是我們可以控制它如何輸出到響應啊!
我們可以定義一個Middleware
,實現如下功能:
判斷當前請求是否
swagger.json
如果是,截獲原始響應,即
swagger.json
文件內容創建一個新的 json 文件,遍歷
swagger.json
所有 JSON 結點,寫入到新 json 文件中如果當前節點是
paths
節點下的子節點,先判斷子節點名稱是否是授權終結點,是則寫入,否則跳過將新 json 文件輸出到響應中
實現
SwaggerEndpointsFilterMiddleware
實現代碼如下:
public?class?SwaggerEndpointsFilterMiddleware
{private?readonly?RequestDelegate?_next;public?SwaggerEndpointsFilterMiddleware(RequestDelegate?next){_next?=?next;}public?async?Task?Invoke(HttpContext?httpContext){if?(httpContext.Request.Path.Value?!=?null?&&?httpContext.Request.Path.HasValue?&&httpContext.Request.Path.Value.Contains("swagger.json",?StringComparison.InvariantCultureIgnoreCase)){var?originalStream?=?httpContext.Response.Body;using?(var?memoryStream?=?new?MemoryStream()){//截獲原始響應,將響應內容寫入?MemoryStreamhttpContext.Response.Body?=?memoryStream;await?_next(httpContext);//獲取當前用戶可訪問的?Endpointsvar?validEndpoints?=?GetValidEndpoints(httpContext.Request);memoryStream.Position?=?0;using?(var?sr?=?new?StreamReader(memoryStream)){//將修改過的?json?寫入響應?await?originalStream.WriteAsync(CreateSwaggerJson(sr.ReadToEnd(),?validEndpoints));}httpContext.Response.Body?=?originalStream;return;}}await?_next(httpContext);}
}
關鍵代碼在CreateSwaggerJson
:
private?byte[]?CreateSwaggerJson(string?json,?IEnumerable<string>?validEndpoints)
{using?(var?memoryStream?=?new?MemoryStream()){using?(var?utf8JsonWriter?=?new?Utf8JsonWriter(memoryStream)){using?(var?jsonDocument?=?JsonDocument.Parse(json)){utf8JsonWriter.WriteStartObject();foreach?(var?element?in?jsonDocument.RootElement.EnumerateObject()){if?(element.Name?==?"paths"){utf8JsonWriter.WritePropertyName(element.Name);utf8JsonWriter.WriteStartObject();//遍歷?paths?子節點,檢查?Endpoint?是否已授權foreach?(var?endpoint?in?element.Value.EnumerateObject()){if?(validEndpoints.Contains(endpoint.Name)){endpoint.WriteTo(utf8JsonWriter);}}utf8JsonWriter.WriteEndObject();}else{element.WriteTo(utf8JsonWriter);}}utf8JsonWriter.WriteEndObject();}}return?memoryStream.ToArray();}
}
最后,修改 Startup.cs:
app.UseMiddleware<SwaggerEndpointsFilterMiddleware>();app.UseSwagger();
app.UseSwaggerUI(c?=>?c.SwaggerEndpoint("/swagger/v1/swagger.json",?"WebApplication1?v1"));
結論
今天,我們通過 Middleware 攔截swagger.json
的輸出,實現了 Swagger UI 僅為用戶暴露已授權終結點。
添加微信號【MyIO666】,邀你加入技術交流群