使用 Swagger 在 ASP.NET Core 中实现 OAuth

Sim*_*aur 8 c# oauth-2.0 swagger .net-core swashbuckle.aspnetcore

我想在我的 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;
        }
Run Code Online (Sandbox Code Playgroud)

代码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"}
                }
            };
        }
Run Code Online (Sandbox Code Playgroud)

通过使用此代码,我在我的 swagger UI 上获得一个“授权”按钮,当我单击该按钮时,我将重定向到我的登录页面(基于角度的前端)。所以我给出了我的AuthorizationUrlashttp://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
                };
            });

Run Code Online (Sandbox Code Playgroud)

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

我从 swagger 重定向到登录页面后形成的网址是:

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
Run Code Online (Sandbox Code Playgroud)

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

Igo*_*yda 9

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

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

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

接下来,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();
    });
}
Run Code Online (Sandbox Code Playgroud)

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

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

  1. CORS 政策
  2. 可用的 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 ...
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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


xcs*_*lab 1

如果您查看 OAuth 网站,该案例被描述为“按请求定制”

\n
\n

按请求定制

\n

通常,开发人员会认为他们需要能够在每个授权请求上使用不同的重定向 URL,并尝试更改每个请求的查询字符串参数。这不是重定向 URL 的预期用途,授权服务器不应允许这样做。服务器应拒绝任何带有与注册 URL 不完全匹配的重定向 URL 的授权请求。

\n
\n
\n

如果客户端希望在重定向 URL 中包含特定于请求的数据,则可以使用 \xe2\x80\x9cstate\xe2\x80\x9d 参数来存储用户重定向后将包含的数据。它可以对状态参数本身中的数据进行编码,也可以使用状态参数作为会话 ID 将状态存储在服务器上。

\n
\n

我希望这对您的探索有所帮助。

\n

来源:https ://www.oauth.com/oauth2-servers/redirect-uris/redirect-uri-registration/

\n