首页 > 解决方案 > 使用 Swagger 在 ASP.NET Core 中实现 OAuth

问题描述

我想在我的 Web 应用程序中实现 OAuth,为此我在我的startup.cs

public static IServiceCollection AddSwaggerDocumentation(this IServiceCollection services)
        {
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "CombiTime API v1.0", Version = "v1" });

                c.AddSecurityDefinition("OAuth2", new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.OAuth2,
                    Flows = new OpenApiOAuthFlows
                    {
                        AuthorizationCode = new OpenApiOAuthFlow
                        {
                            AuthorizationUrl = new Uri("http://localhost:4200/login"),
                            TokenUrl = new Uri("http://localhost:4200/connect/token")
                        }
                    }
                });
                c.OperationFilter<AuthorizeOperationFilter>();

                c.AddSecurityRequirement(new OpenApiSecurityRequirement{
                    {
                        new OpenApiSecurityScheme{
                            Reference = new OpenApiReference{
                                Id = "Bearer", //The name of the previously defined security scheme.
                                Type = ReferenceType.SecurityScheme
                            }
                        },new List<string>()
                    }
                });
            });

            return services;
        }

        public static IApplicationBuilder UseSwaggerDocumentation(this IApplicationBuilder app)
        {
            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "Versioned API v1.0");
                c.DocumentTitle = "Title Documentation";
                c.DocExpansion(DocExpansion.None);
                c.RoutePrefix = string.Empty;
                c.OAuthClientId("combitimeapi_swagger");
                c.OAuthAppName("Combitime API");
                c.OAuthUsePkce();
            });

            return app;
        }

AuthorizeOperationFilter代码如下:

public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            // Since all the operations in our api are protected, we need not
            // check separately if the operation has Authorize attribute
            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[] {"combitimeapi"}
                }
            };
        }

通过使用此代码,我在我的招摇 UI 上获得了一个“授权”按钮,当我单击该按钮时,我将重定向到我的登录页面(基于 Angular 的前端)。所以我给了我AuthorizationUrl的 ashttp://localhost:4200/login然后当我被重定向到登录页面时,我使用有效凭据登录,我使用 jwt 令牌进行登录,为此我在我的startup.cs

services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(x =>
            {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = false,
                    ValidateAudience = false
                };
            });

我想在使用有效凭据登录后重定向回招摇 UI,但问题是我在登录后被重定向到仪表板。请帮助我或让我知道我做错了什么。

我从 swagger 重定向到登录页面后正在形成的 url 是:

http://localhost:4200/login?response_type=code&client_id=combitimeapi_swagger&redirect_uri=http:%2F%2Flocalhost:61574%2Foauth2-redirect.html&state=V2VkIEZlYiAxNyAyMDIxIDIyOjU3OjQ2IEdNVCswNTMwIChJbmRpYSBTdGFuZGFyZCBUaW1lKQ%3D%3D&code_challenge=mT0amBTJgczCZmNSZAYVfjzzpaTiGb68XlyR3RNHuas&code_challenge_method=S256

我的前端在端口 4200 上运行。我的招摇在端口 61574 上运行。但是在输入有效凭据后我没有被重定向到招摇 UI 请帮助我。

标签: c#.net-coreoauth-2.0swaggerswashbuckle.aspnetcore

解决方案


首先,让我为您的图片添加一些细节:

  1. 您有两个应用程序,一个带有 API(基于 ASP.NET Core),一个带有前端 UI(Angular,但没关系),而且很重要的是,带有授权/身份验证功能。
  2. 您使用 .NETCore 3.1
  3. 您为 swagger 配置授权,这意味着来自 swagger UI 页面的任何调用都将使用给定的授权参数。

因此,对于 API 应用程序,我们必须添加一个具有帮助方法的类来配置我们的招摇:

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddSwaggerDocumentation(this IServiceCollection services)
    {
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo { Title = "CombiTime API v1.0", Version = "v1" });

            c.AddSecurityDefinition(
                "oauth2", 
                new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.OAuth2,
                    Flows = new OpenApiOAuthFlows
                    {
                        AuthorizationCode = new OpenApiOAuthFlow
                        {
                            AuthorizationUrl = new Uri("https://lvh.me:4201/connect/authorize"),
                            TokenUrl = new Uri("https://lvh.me:4201/connect/token"),
                            Scopes = new Dictionary<string, string> {
                                { "combitimeapi", "Demo API" }
                            }
                        }
                    }
                });
            c.OperationFilter<AuthorizeOperationFilter>();

            c.AddSecurityRequirement(
                new OpenApiSecurityRequirement 
                {
                    {
                        new OpenApiSecurityScheme{
                            Reference = new OpenApiReference{
                                Id = "oauth2", //The name of the previously defined security scheme.
                                Type = ReferenceType.SecurityScheme
                            }
                        },
                        new List<string>()
                    }
                });
        });

        return services;
    }

    public static IApplicationBuilder UseSwaggerDocumentation(this IApplicationBuilder app)
    {
        app.UseSwagger();
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "Versioned API v1.0");
            c.DocumentTitle = "Title Documentation";
            c.DocExpansion(DocExpansion.None);
            c.RoutePrefix = string.Empty;
            c.OAuthClientId("combitimeapi_swagger");
            c.OAuthAppName("Combitime API");
            c.OAuthScopeSeparator(",");
            c.OAuthUsePkce();
        });

        return app;
    }
}

请注意AuthorizationUrl财产和TokenUrl财产。该AuthorizationUrl属性应指向我们的 OAuth2 服务器授权端点。请记住,授权端点和登录页面是不同的端点。我们可以通过访问 url 获取前端应用程序的所有已知端点:https://lvh.me:4201/.well-known/openid-configuration以防我们的应用程序使用 ASP.NET Core 和 IdentityServer。

接下来,Startup.cs我们的 API 应用程序应该包含:

public void ConfigureServices(IServiceCollection services)
{
    // ... some your code ...

    services.AddSwaggerDocumentation();
    services.AddAuthentication("Bearer")
        .AddIdentityServerAuthentication("Bearer", options =>
        {
            options.ApiName = "combitimeapi";
            options.Authority = "https://lvh.me:4201";
        });

    // ... some your code ...
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ... some your code ...
    app.UseSwaggerDocumentation();
    app.UseRouting();
    app.UseAuthorization();

    // ... some your code ...

    app.UseEndpoints(endpoints =>
    {
         endpoints.MapControllers();
    });
}

请不要忘记为[Authorize]所有控制器添加属性,因为您AuthorizeOperationFilter假设已经完成。

让我们为我们的前端和授权部分寻找所需的更改。您应该配置一些特定的东西,例如:

  1. CORS 政策
  2. Awailable API 客户端(一个是您的 Angular UI,另一个是 API 应用程序)
  3. 可用的 API 资源
  4. 身份验证和授权方法

该类Startup.cs应包含:

public void ConfigureServices(IServiceCollection services)
{
    // ... some your code ...

    services.AddCors(policies => {
        policies.AddDefaultPolicy(builder => {
            builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin();
        });
    });

    services.AddIdentityServer()
        .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
            options.Clients.AddIdentityServerSPA("forntend", cfg => {});
            options.Clients.AddNativeApp("combitimeapi_swagger", cfg => {
                cfg
                    .WithRedirectUri("https://lvh.me:5001/oauth2-redirect.html")
                    .WithScopes("combitimeapi");
            });
            options.ApiResources.AddApiResource("combitimeapi", cfg => {
                cfg.WithScopes("combitimeapi");
            });
        })
        .AddApiResources();

    services
        .AddAuthentication(
            x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
        .AddIdentityServerJwt();
    // ... some your code ...
}

我在这里使用.AddIdentityServerJwt()而不是您的.AddJwtBearer(...),因为我没有您的密钥和其他特定选项。

前端应用程序配置为使用端口 4201 用于 HTTPS 和 4200 用于 HTTP,API 应用程序配置为使用端口 5001 用于 HTTPS 和 5000 用于 HTTP。

现在您可以运行这两个应用程序并转到页面https://lvh.me:5001/index.html并按“授权”按钮以获取如下对话框:身份验证对话框

输入您的秘密,标记范围并按“授权”,在您进行身份验证后,您将获得:身份验证对话框

如果您没有获得成功的结果,请查看前端应用程序的日志,通常它包含可以帮助您找出问题的错误。

希望以上文字对您有所帮助。


推荐阅读