Asp.Net Core: Swagger 與 Identity Server 4

lkptantan發表於2022-02-23

Swagger不用多說,可以自動生成Web Api的介面文件和客戶端呼叫程式碼,方便開發人員進行測試。通常我們只需要幾行程式碼就可以實現這個功能:

...
builder.Services.AddSwaggerGen();
...
app.UseSwagger();
app.UseSwaggerUI();
...

可如果使用Identity Server 4等認證服務對Web Api進行保護後,使用上面程式碼生成的Web Api文件就無法工作了,在進行測試時會提示401錯誤。

本文介紹如何使Swagger在Identity Server 4認證服務保護下工作。

配置Identity Server 4

首先,使用開源的Identity Server 4 Admin搭建認證服務,這部分工作在系列文章《Identity Server 4 從入門到落地》中有詳細的介紹,這裡不再重複。

準備測試用Web Api

接下來,使用Visual Studio 2022建立一個Asp.Net Core Web Api專案,我們使用專案中預設的Web Api進行測試。在專案中增加Identity Server 4對Web Api的保護,這部分的詳細介紹參見《Identity Server 4 從入門到落地(十)—— 編寫可配置的客戶端和Web Api》,這裡只列出必要的步驟。
1、使用Nuget包管理器安裝ZL.IdentityServer4ClientConfig和ZL.SameSiteCookiesService
2、修改Program.cs,增加如下程式碼:

...
builder.Services.AddIdentityServer4Api(builder.Configuration);//增加程式碼
builder.Services.AddSameSiteSupport();;//增加程式碼
...
app.UseCors("cors");//增加程式碼
app.UseAuthentication(); //增加程式碼
app.UseAuthorization();

3、在appSettings.json中增加相應的配置:
"IdentityServer4Api": {
"Authority": "http://host.docker.internal:4010",
"CorsOrgins": [
"http://host.docker.internal:5291"
],
"Policies": [
{
"Name": "ApiScope",
"RequireAuthenticatedUser": "true",
"Claims": [
{
"ClaimType": "scope",
"AllowValues": [ "testapi" ]
}
]
}
],
這裡定義的認證伺服器地址需要根據實際情況進行修改,CorsOrgins中設定的是允許訪問的客戶端的Uri。這裡定義的允許訪問的scope是testapi,需要根據實際進行修改。
4、為Web Api的Action增加[Authorize]標籤:

        [Authorize]
        [HttpGet(Name = "GetWeatherForecast")]
        public IEnumerable<WeatherForecast> Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }

在認證中心增加Web Api Scope和Swagger Client

現在我們在認證中心增加Web Api Scope和Swagger Client。
首先,增加Web Api Scope,在配置檔案中定義的允許訪問的scope是testapi,所以在這裡增加的Scope名稱必須為testapi。

然後,還需要為Swagger 增加一個客戶端,名稱為demo_api_swagger:

客戶端定義中有幾個地方需要注意,首先是允許作用域必須與前面定義的相同,這裡是testapi:

需要客戶端金鑰設定為false:

還有重定向的Url,Swagger針對oauth 2.0的重定向地址是swagger/oauth2-redirect.html

最後,不要忘記在令牌分頁中設定CORS:

修改Swagger相關程式碼

在認證中心設定完成後,還需要修改Web Api專案中Swagger相關的程式碼,使Swagger支援認證。
首先需要增加一個類,實現IOperationFilter介面:

using Microsoft.AspNetCore.Authorization;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace WebApiIDS4Demo
{
    public class AuthorizeCheckOperationFilter : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            var hasAuthorize =
              context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any()
              || context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();

            if (hasAuthorize)
            {
                operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
                operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" });

                operation.Security = new List<OpenApiSecurityRequirement>
            {
                new OpenApiSecurityRequirement
                {
                    [
                        new OpenApiSecurityScheme {Reference = new OpenApiReference
                        {
                            Type = ReferenceType.SecurityScheme,
                            Id = "oauth2"}
                        }
                    ] = new[] {"api1"}
                }
            };

            }
        }
    }
}

然後,在Program.cs中修改Swagger相關的程式碼:

...
builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo { Title = "Protected API", Version = "v1" });
    var strurl = builder.Configuration["IdentityServer4Api:Authority"];
    options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.OAuth2,
        Flows = new OpenApiOAuthFlows
        {
            AuthorizationCode = new OpenApiOAuthFlow
            {
                AuthorizationUrl = new Uri( strurl +"/connect/authorize"),
                TokenUrl = new Uri(strurl + "/connect/token"),
                Scopes = new Dictionary<string, string>
            {
                {"testapi", "Demo API - full access"}
            }
            }
        }
    });
    options.OperationFilter<AuthorizeCheckOperationFilter>();
});
...
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
        options.OAuthClientId("demo_api_swagger");
        options.OAuthAppName("Demo API - Swagger");
        options.OAuthUsePkce();
    });
}

改造完成,可以進行測試了,執行Web Api專案,預設進入Swagger文件頁面,我們會發現多了一個Authorize按鈕,並且Web Api方法旁邊會有一個加鎖的圖示:

這時,如果訪問Web Api,會出現401錯誤,我們需要先進行認證再訪問,點選加鎖的圖示,彈出認證介面:

點選認證按鈕,如果順利的話,會出現認證完成的介面:

點選Close關閉視窗,會發現加鎖圖示發生了變化:

這時,再次訪問Web Api就可以正常返回資料了:

相關文章