IdentityServer4资源所有者密码和Win身份验证:未经授权

时间:2019-07-30 13:52:29

标签: c# asp.net-core identityserver4 windows-authentication

最近几天,我一直在阅读IdentityServer4文档,并使用资源所有者密码将示例服务器+示例客户端放在一起。现在,我想并行添加Windows身份验证(将通过Active Directory完成),因此客户端应用程序(不是Web应用程序,而是桌面应用程序)可以提示用户输入凭据,也可以使用Windows身份验证通过Active Directory登录。

有关Windows身份验证的文档介绍了如何配置IIS或HTTP.Sys,但是我想要做的是:

  1. 用户打开应用程序
  2. 该应用使用单点登录将请求发布到Web api以请求令牌和刷新令牌
  3. Web api使用Windows身份验证来验证用户身份并返回令牌

我已经尝试遵循此answer,但是它不起作用(返回未经授权的内容)。

网络api:Startup.cs

    public class Startup
    {

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvcCore()
                .AddAuthorization()
                .AddJsonFormatters();


            services.AddAuthentication("Bearer")    
            .AddJwtBearer(options =>
            {
                // base-address of your identityserver
                options.Authority = "http://localhost:5000";
                options.RequireHttpsMetadata = false;
                // name of the API resource
                options.Audience = "api/user";
            });

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            services.AddIdentityServer(options => { options.PublicOrigin = "http://localhost:5000"; options.MutualTls.Enabled = false; })                
                .AddExtensionGrantValidator<WinAuthGrantValidator>()
                .AddDeveloperSigningCredential()
                .AddTestUsers(Config.GetUsers())
                .AddInMemoryApiResources(Config.GetApiResources())
                .AddInMemoryClients(Config.GetClients())
                .AddInMemoryIdentityResources(Config.GetIdentityResources());

        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env,
            ILogger<Startup> logger, IServer server)
        {
            app.Use(async (context, next) =>
            {
                context.Features.Get<IHttpMaxRequestBodySizeFeature>()
                    .MaxRequestBodySize = 10 * 1024;

                var serverAddressesFeature =
                    app.ServerFeatures.Get<IServerAddressesFeature>();
                var addresses = string.Join(", ", serverAddressesFeature?.Addresses);

                logger.LogInformation($"Addresses: {addresses}");

                await next.Invoke();

            });
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");

                app.UseHsts();
            }

            // Enable HTTPS Redirection Middleware when hosting the app securely.
            //app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseMvc();
            app.UseIdentityServer();
            app.UseAuthentication();
        }
    }

    internal static class Config
    {
        public static List<TestUser> GetUsers()
        {
            return new List<TestUser>
            {
                new TestUser
                {
                    SubjectId = "1",
                    Username = "alice",
                    Password = "password"
                },
                new TestUser
                {
                    SubjectId = "2",
                    Username = "bob",
                    Password = "password"
                }
            };
        }

        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                // other clients omitted...

                // resource owner password grant client
                new Client
                {
                    ClientId = "ro.client",
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },
                   // AllowedScopes = { "api1" }
                   AllowedScopes = { "api/user" }
                },
            new Client
            {
                ClientId = "winauth",
                AllowedGrantTypes =  new List<string>{ "windows_auth" },

                ClientSecrets =
                {
                    new Secret("secret".Sha256())
                },
               // AllowedScopes = { "api1" }
               AllowedScopes = { "api/user" }
             }
            };
        }

        internal static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource { Name = "api1",Scopes = new List<Scope> { new Scope {  Name = "api1",
                    DisplayName = "Full access to API 2"}  }, Enabled = true,  ApiSecrets = new List<Secret>
                    {
                        new Secret("secret".Sha256())
                    }
                },
                new ApiResource { Name = "api/user",Scopes = new List<Scope> { new Scope {  Name = "api/user",
                    DisplayName = "Full access to API 2"}  }, Enabled = true,  ApiSecrets = new List<Secret>
                    {
                        new Secret("secret".Sha256())
                    }
                }};
        }


        public static List<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile()
            };
        }
    }



public class WinAuthGrantValidator : IExtensionGrantValidator
    {
    private readonly HttpContext httpContext;

    public string GrantType => "windows_auth";

    public WinAuthGrantValidator(IHttpContextAccessor httpContextAccessor)
    {
        httpContext = httpContextAccessor.HttpContext;
    }

    public async Task ValidateAsync(ExtensionGrantValidationContext context)
    {
        // see if windows auth has already been requested and succeeded
        var result = await httpContext.AuthenticateAsync("Windows");
        if (result?.Principal is WindowsPrincipal wp)
        {
            context.Result = new GrantValidationResult(wp.Identity.Name, GrantType, wp.Claims);
        }
        else
        {
            // trigger windows auth
            await httpContext.ChallengeAsync("Windows");
            context.Result = new GrantValidationResult { IsError = false, Error = null, Subject = null };
        }
    }
}
}

网络API:Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        var isService = !(Debugger.IsAttached || args.Contains("--console"));

        if (isService)
        {
            var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
            var pathToContentRoot = Path.GetDirectoryName(pathToExe);
            Directory.SetCurrentDirectory(pathToContentRoot);
        }

        var builder = CreateWebHostBuilder(
            args.Where(arg => arg != "--console").ToArray());

        var host = builder.Build();

        if (isService)
        {
            // To run the app without the CustomWebHostService change the
            // next line to host.RunAsService();
            host.RunAsCustomService();
        }
        else
        {
            host.Run();
        }
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureLogging((hostingContext, logging) =>
            {
                logging.AddEventLog();
            })
            .ConfigureAppConfiguration((context, config) =>
            {
                // Configure the app here.
            })
            .UseStartup<Startup>()
            .UseHttpSys(options =>
            {
                options.AllowSynchronousIO = true;
                options.Authentication.Schemes = Microsoft.AspNetCore.Server.HttpSys.AuthenticationSchemes.Kerberos | Microsoft.AspNetCore.Server.HttpSys.AuthenticationSchemes.NTLM;
                options.Authentication.AllowAnonymous = true;
                options.MaxConnections = null;
                options.MaxRequestBodySize = 30000000;
                options.UrlPrefixes.Add("http://localhost:5000");
            });
}

web API UserController.cs

[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = "Bearer")]
[ApiController]
public class UserController : ControllerBase
{
    // GET api/user
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        return new string[] { User.Identity.Name, User.Identity.AuthenticationType };
    }
}

客户代码:

using (var client = new HttpClient())
{
    disco = await client.GetDiscoveryDocumentAsync(new   DiscoveryDocumentRequest
    {
        Address = baseUrl,
        Policy = { RequireHttps = false }
    });
    if (disco.IsError)
    {
        Console.WriteLine(disco.Error);
        Console.ReadLine();
        return;
     }
     var httpHandler = new HttpClientHandler
     {
         UseDefaultCredentials = true,
     };

     using (var client = new HttpClient())
     {
         // request token
         TokenResponse tokenResponse = await client.RequestTokenAsync(new TokenRequest
         {
             GrantType = "windows_auth",
             Address = disco.TokenEndpoint,
             ClientId = "winauth",
             ClientSecret = "secret"
         });

         if (tokenResponse.IsError)
         {
             Console.WriteLine(tokenResponse.Error);
             Console.ReadLine();
            return;
         }
     }

它未经授权就返回。

1 个答案:

答案 0 :(得分:0)

我找到了解决方案:我需要使用SubjectId = MYDOMAIN\myusername配置一个TestUser,然后它才能工作。

得到的错误过于混乱。

相关问题