.net core 2.2多种身份验证方案

时间:2019-10-22 15:45:58

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

我正在尝试制作带有身份验证的.net核心Web API应用程序。 我需要实现Windows身份验证,cookie身份验证,ldap活动目录身份验证和jwt承载身份验证。 每个身份验证单独都能正常工作,但是如果我尝试同时启用所有身份验证,它将无法按我期望的那样工作。 为了进行Windows身份验证,我编写了一个自定义ForwardDefaultSelector,如下所示:

services.AddAuthentication(optons =>
           {
               optons.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               optons.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               optons.DefaultForbidScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               optons.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               optons.DefaultAuthenticateScheme = "smart";
               optons.DefaultChallengeScheme = "smart";
               optons.DefaultScheme = "smart";
               //optons.AddScheme("Windows", config => config.HandlerType = IISDefaults.)
           })
           .AddPolicyScheme("smart", "", options =>
           {
               options.ForwardDefaultSelector = context =>
               {
                   var authHeader = context.Request.Headers[HeaderNames.Authorization].FirstOrDefault();
                   if (!string.IsNullOrEmpty(authHeader))
                   {
                       if (authHeader.StartsWith(JwtBearerDefaults.AuthenticationScheme) == true)
                           return JwtBearerDefaults.AuthenticationScheme;
                       else if (authHeader.StartsWith(IISDefaults.Negotiate) == true || authHeader.StartsWith(IISDefaults.Ntlm) == true)
                           return IISDefaults.AuthenticationScheme;

                   }
                   else
                   {
                       if (context.Request.Cookies.ContainsKey(string.IsNullOrEmpty(Configuration["ApplicationName"]) ? ".Authentication.Aion" : ".Authentication." + Configuration["ApplicationName"]))
                       {
                           return CookieAuthenticationDefaults.AuthenticationScheme;
                       }
                       else if (context.Request.Cookies.ContainsKey("LDAP_AUTH"))
                       {
                           return LdapAuthenticationDefaults.AuthenticationScheme;
                       }
                       else
                       {
                           return IISDefaults.AuthenticationScheme;
                       }
                   }
                   return CookieAuthenticationDefaults.AuthenticationScheme;

               };
           })
           .AddCookie(options =>
           {
               options.Cookie.Name = string.IsNullOrEmpty(Configuration["ApplicationName"]) ? ".Authentication.Aion" : ".Authentication." + Configuration["ApplicationName"];
               //options.Cookie.HttpOnly = true;
               options.Cookie.SecurePolicy = CookieSecurePolicy.None;
               options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Lax;
               options.SlidingExpiration = true;
               options.LoginPath = new PathString("/Account/Login");
               options.LogoutPath = new PathString("/Account/Logout");
               options.AccessDeniedPath = new PathString("/Account/Authorize");
               var expirationDays = Configuration.GetValue<int>("DefaultExpirationDays");
               options.ExpireTimeSpan = TimeSpan.FromDays(expirationDays);
               options.Events.OnValidatePrincipal = async (context) =>
               {
                   await SecurityStampValidator.ValidatePrincipalAsync(context);
               };
               //options.Events.OnSignedIn = context =>
               //{
               //    return Task.CompletedTask;
               //};
               //options.ForwardAuthenticate = "Ldap";

           })
           .AddJwtBearer(options =>
           {
               options.RequireHttpsMetadata = false;
               //options.SaveToken
               var session = Configuration.GetSection("BearerAuthenticationConfigs");

               options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
               {
                   ValidateIssuer = false,
                   ValidateAudience = false,
                   ValidateLifetime = true,
                   ValidateIssuerSigningKey = true,
                   RequireSignedTokens = false,
                   //ValidAudience = "http://localhost:5000",
                   //ValidIssuer = "http://localhost:5000",
                   RequireExpirationTime = false,
                   IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(session.GetValue<string>("JwtSecret"))),
                   ClockSkew = TimeSpan.Zero
               };
               options.Authority = "http://localhost:5000";
               options.Configuration = new Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfiguration
               {
                   TokenEndpoint = new PathString("/Token"),
                   AuthorizationEndpoint = new PathString("/Account/Authorize")

               };
               options.EventsType = typeof(JWTValidationEvents);
           })
           .AddScheme<LdapAuthenticationSchemeOptions, LdapAuthenticationHandler>(LdapAuthenticationDefaults.AuthenticationScheme, options =>
           {
               var configSession = Configuration.GetSection("LdapAuthenticationConfigs");

               options.LdapHost = configSession.GetValue<string>("Host", "localhost");
               options.LdapPort = configSession.GetValue<int>("Port", 389);
               var expireDays = configSession.GetValue<int?>("ExpirationDays", Configuration.GetValue<int>("DefaultExpirationDays"));
               options.Expire = expireDays.HasValue ? TimeSpan.FromDays(expireDays.Value) : (TimeSpan?)null;
               options.UserDomain = configSession.GetValue<string>("UserDomain", "");
           });

和web.config以在IIS上启用Windows身份验证

<?xml version="1.0" encoding="utf-8"?>
<configuration>

  <!-- To customize the asp.net core module uncomment and edit the following section. 
  For more info see https://go.microsoft.com/fwlink/?linkid=838655 -->

  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <security>
        <authentication>
         <!--I need to enable anonymous authentication for token call-->
          <anonymousAuthentication enabled="true" />
          <windowsAuthentication enabled="true" />
        </authentication>
      </security>
    </system.webServer>
  </location>

</configuration>

用于管理Windows身份验证的自定义中间件为:

public class WindowsLoginMiddleware
    {
        private readonly RequestDelegate _next;
        //private readonly ILogger _logger;
        private readonly ILogger<WindowsLoginMiddleware> _logger;




        public WindowsLoginMiddleware(RequestDelegate next, ILogger<WindowsLoginMiddleware> Logger, ApplicationDbContext db, IHttpContextAccessor httpcontextaccessor)
        {
            _next = next;
            _logger = Logger;
            _db = db;
            _httpContextAccessor = httpcontextaccessor;
        }

        public async Task InvokeAsync(HttpContext context, UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
        {
            if (signInManager.IsSignedIn(context.User))
            {
                _logger.LogInformation("User already signed in");
            }
            else
            {
                if (context.User.Identity as WindowsIdentity != null)
                {
                    _logger.LogInformation($"User with Windows Login {context.User.Identity.Name} needs to sign in");
                    var windowsUsername = context.User.Identity.Name;
                    //Here I create principal claim from db application user and save on cookie for next requests
                    var identity = await WindowsAuthenticationClaimProvider.GetIdentityClaimsAsync(windowsUsername, string.Empty, true);
                    if(identity == null)
                    {
                        _logger.LogInformation($"User {context.User.Identity.Name} has no role in system");
                        context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                        await context.Response.WriteAsync("");
                        return;
                    }
                    else
                    {
                        _logger.LogDebug($"User with username {windowsUsername} successfully signed in");
                    }
                }
            }

            // Pass the request to the next middleware
            await _next(context);
        }
    }

它可以工作,但是我不喜欢自定义ForwardDefaultSelector,因为所有请求都具有默认的IISDefaults.AuthenticationScheme,并且Windows身份验证进行了两次调用以获取WindowsIdentity(在{{1}上传递两次) }。

有人可以更好地解决这种情况吗? 谢谢。

0 个答案:

没有答案