使用用户名和密码 Asp Net Core 2.1 保护 swagger 文档页面

Posted

技术标签:

【中文标题】使用用户名和密码 Asp Net Core 2.1 保护 swagger 文档页面【英文标题】:Secure swagger docs page with username and password Asp Net Core 2.1 【发布时间】:2018-12-23 10:08:20 【问题描述】:

我正在使用带有 Swashbuckle.aspnetcore.swagger 的 Asp.Net Core 2.1 Web Api

我想在授予访问权限之前使用用户名和密码保护 api 文档页面。

示例文档页面

为了确保它不被公众访问

【问题讨论】:

How to secure generated API documentation using swagger swashbuckle的可能重复 【参考方案1】:

复制自mguinness's answer on Github:


在 .NET Core 中,您使用中间件,而不是 DelegatingHandler:

public class SwaggerAuthorizedMiddleware

    private readonly RequestDelegate _next;

    public SwaggerAuthorizedMiddleware(RequestDelegate next)
    
        _next = next;
    

    public async Task Invoke(HttpContext context)
    
        if (context.Request.Path.StartsWithSegments("/swagger")
            && !context.User.Identity.IsAuthenticated)
        
            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
            return;
        

        await _next.Invoke(context);
    

您还需要一个扩展方法来帮助添加到管道:

public static class SwaggerAuthorizeExtensions

    public static IApplicationBuilder UseSwaggerAuthorized(this IApplicationBuilder builder)
    
        return builder.UseMiddleware<SwaggerAuthorizedMiddleware>();
    

然后在使用 Swagger 之前添加到 Startup.cs 中的 Configure 方法:

app.UseSwaggerAuthorized();
app.UseSwagger();
app.UseSwaggerUi();

那里还发布了一个变体解决方案how to do it with basic auth。

【讨论】:

知道为什么这不会像控制器操作那样重定向回我的登录页面吗?【参考方案2】:

我在 GitHub 上找到了一个解决方案并将其应用到我的项目中。它按预期工作。

以下代码复制自https://github.com/domaindrivendev/Swashbuckle.WebApi/issues/384#issuecomment-410117400

public class SwaggerBasicAuthMiddleware

    
private readonly RequestDelegate next;

    public SwaggerBasicAuthMiddleware(RequestDelegate next)
    
        this.next = next;
    

    public async Task InvokeAsync(HttpContext context)
    
        //Make sure we are hitting the swagger path, and not doing it locally as it just gets annoying :-)
        if (context.Request.Path.StartsWithSegments("/swagger") && !this.IsLocalRequest(context))
        
            string authHeader = context.Request.Headers["Authorization"];
            if (authHeader != null && authHeader.StartsWith("Basic "))
            
                // Get the encoded username and password
                var encodedUsernamePassword = authHeader.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries)[1]?.Trim();

                // Decode from Base64 to string
                var decodedUsernamePassword = Encoding.UTF8.GetString(Convert.FromBase64String(encodedUsernamePassword));

                // Split username and password
                var username = decodedUsernamePassword.Split(':', 2)[0];
                var password = decodedUsernamePassword.Split(':', 2)[1];

                // Check if login is correct
                if (IsAuthorized(username, password))
                
                    await next.Invoke(context);
                    return;
                
            

            // Return authentication type (causes browser to show login dialog)
            context.Response.Headers["WWW-Authenticate"] = "Basic";

            // Return unauthorized
            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
        
        else
        
            await next.Invoke(context);
        
    

    public bool IsAuthorized(string username, string password)
    
        // Check that username and password are correct
        return username.Equals("SpecialUser", StringComparison.InvariantCultureIgnoreCase)
                && password.Equals("SpecialPassword1");
    

    public bool IsLocalRequest(HttpContext context)
    
        //Handle running using the Microsoft.AspNetCore.TestHost and the site being run entirely locally in memory without an actual TCP/IP connection
        if (context.Connection.RemoteIpAddress == null && context.Connection.LocalIpAddress == null)
        
            return true;
        
        if (context.Connection.RemoteIpAddress.Equals(context.Connection.LocalIpAddress))
        
            return true;
        
        if (IPAddress.IsLoopback(context.Connection.RemoteIpAddress))
        
            return true;
        
        return false;
    

public static class SwaggerAuthorizeExtensions
    
        public static IApplicationBuilder UseSwaggerAuthorized(this IApplicationBuilder builder)
        
            return builder.UseMiddleware<SwaggerBasicAuthMiddleware>();
        
    

在 Startup.cs 中

app.UseAuthentication(); //Ensure this like is above the swagger stuff

app.UseSwaggerAuthorized();
app.UseSwagger();
app.UseSwaggerUI();

【讨论】:

欢迎提供解决方案链接,但请确保您的答案在没有它的情况下有用:add context around the link 这样您的其他用户就会知道它是什么以及为什么会出现,然后引用最相关的您链接到的页面的一部分,以防目标页面不可用。 Answers that are little more than a link may be deleted. 工作完美!非常感谢! ^^

以上是关于使用用户名和密码 Asp Net Core 2.1 保护 swagger 文档页面的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 2.1 在视图中使用 HttpContext

ASP.NET Core 2.1 Jwt 设置自定义声明

没有密码的 ASP.Net Core 中的 Active Directory 授权

ASP.NET Core Identity 系列之三

用户在 ASP.NET Core Identity 中打开设置页面时如何询问密码?

在 ASP.NET Core 中使用基于本地存储的 JWT-Token 更改用户密码(ASP.Identity)