Quería implementar la autenticación de formularios con membresía en mi aplicación asp.net MVC Core. Teníamos la configuración de autenticación de formularios en nuestra aplicación anterior como a continuación y queríamos usar la misma en .net core.

  [HttpPost]
public ActionResult Login(LoginModel model, string returnUrl)
{
  if (!this.ModelState.IsValid)
  {
      return this.View(model);
   }

   //Authenticate
   if (!Membership.ValidateUser(model.UserName, model.Password))
   {
       this.ModelState.AddModelError(string.Empty, "The user name or 
   password provided is incorrect.");
   return this.View(model);
   }
   else
   {
       FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
        return this.RedirectToAction("Index", "Home");
   }
 return this.View(model);
   }

En mi config:

  <membership defaultProvider="ADMembership">
       <providers>
         <add name="ADMembership" 
           type="System.Web.Security.ActiveDirectoryMembershipProvider" 
           connectionStringName="ADConnectionString" 
           attributeMapUsername="sAMAccountName" />
       </providers>
   </membership>

Así que estamos usando el directorio activo aquí como miembro.

¿Sigue siendo aplicable en .net core.

Si no, qué más está disponible en .net core para la autenticación de formularios y AD.

Agradecería las aportaciones.

0
aman 20 oct. 2017 a las 19:59

3 respuestas

La mejor respuesta

Sí, puede hacerlo en la aplicación Core MVC. Habilita la autenticación de formularios y usa LDAP como almacén de usuarios en el back-end.

Así es como configuro las cosas, para empezar:

Startup.cs

public class Startup
{
    ...
    public void ConfigureServices(IServiceCollection services)
    {
        ...
        // Read LDAP settings from appsettings
        services.Configure<LdapConfig>(this.Configuration.GetSection("ldap"));

        // Define an interface for authentication service,
        // We used Novell.Directory.Ldap as implementation.
        services.AddScoped<IAuthenticationService, LdapAuthenticationService>();

        // Global filter is enabled to protect the whole site
        services.AddMvc(config =>
        {
            var policy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
            config.Filters.Add(new AuthorizeFilter(policy));
            ...
        });

        // Form authentication and cookies settings
        var cookiesConfig = this.Configuration.GetSection("cookies").Get<CookiesConfig>();
        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie(options =>
        {
            options.Cookie.Name = cookiesConfig.CookieName;
            options.LoginPath = cookiesConfig.LoginPath;
            options.LogoutPath = cookiesConfig.LogoutPath;
            options.AccessDeniedPath = cookiesConfig.AccessDeniedPath;
            options.ReturnUrlParameter = cookiesConfig.ReturnUrlParameter;
        });
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        // Redirects all HTTP requests to HTTPS
        if (env.IsProduction())
        {
            app.UseRewriter(new RewriteOptions()
                .AddRedirectToHttpsPermanent());
        }

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

        app.UseStaticFiles();

        app.UseStatusCodePagesWithReExecute("/error", "?code={0}");

        app.UseAuthentication();

        app.UseMvc(routes =>
        {
            ...
        });
    }
}

Appsettings.json

{
  "connectionStrings": {
    "appDbConnection": xxx
  },
  "ldap": {
    "url": "xxx.loc",
    "bindDn": "CN=Users,DC=xxx,DC=loc",
    "username": "xxx",
    "password": "xxx",
    "searchBase": "DC=xxx,DC=loc",
    "searchFilter": "(&(objectClass=user)(objectClass=person)(sAMAccountName={0}))"
  },
  "cookies": {
    "cookieName": "xxx",
    "loginPath": "/account/login",
    "logoutPath": "/account/logout",
    "accessDeniedPath": "/account/accessDenied",
    "returnUrlParameter": "returnUrl"
  }
}

IAuthenticationService.cs

namespace DL.SO.Services.Core
{
    public interface IAuthenticationService
    {
        IAppUser Login(string username, string password);
    }
}

LdapAuthenticationService.cs

Implementación de Ldap del servicio de autenticación, utilizando la biblioteca Novell.Directory.Ldap para comunicarse con el directorio activo. Puedes Nuget esa biblioteca.

using Microsoft.Extensions.Options;
using Novell.Directory.Ldap;
...
using DL.SO.Services.Core;

namespace DL.SO.Services.Security.Ldap
{
    public class LdapAuthenticationService : IAuthenticationService
    {
        private const string MemberOfAttribute = "memberOf";
        private const string DisplayNameAttribute = "displayName";
        private const string SAMAccountNameAttribute = "sAMAccountName";
        private const string MailAttribute = "mail";

        private readonly LdapConfig _config;
        private readonly LdapConnection _connection;

        public LdapAuthenticationService(IOptions<LdapConfig> configAccessor)
        {
            // Config from appsettings, injected through the pipeline
            _config = configAccessor.Value;
            _connection = new LdapConnection();
        }

        public IAppUser Login(string username, string password)
        {
            _connection.Connect(_config.Url, LdapConnection.DEFAULT_PORT);
            _connection.Bind(_config.Username, _config.Password);

            var searchFilter = String.Format(_config.SearchFilter, username);
            var result = _connection.Search(_config.SearchBase, LdapConnection.SCOPE_SUB, searchFilter,
            new[] { MemberOfAttribute, DisplayNameAttribute, SAMAccountNameAttribute, MailAttribute }, false);

            try
            {
                var user = result.next();
                if (user != null)
                {
                    _connection.Bind(user.DN, password);
                    if (_connection.Bound)
                    {
                        var accountNameAttr = user.getAttribute(SAMAccountNameAttribute);
                        if (accountNameAttr == null)
                        {
                            throw new Exception("Your account is missing the account name.");
                        }

                        var displayNameAttr = user.getAttribute(DisplayNameAttribute);
                        if (displayNameAttr == null)
                        {
                            throw new Exception("Your account is missing the display name.");
                        }

                        var emailAttr = user.getAttribute(MailAttribute);
                        if (emailAttr == null)
                        {
                            throw new Exception("Your account is missing an email.");
                        }

                        var memberAttr = user.getAttribute(MemberOfAttribute);
                        if (memberAttr == null)
                        {
                            throw new Exception("Your account is missing roles.");
                        }

                        return new AppUser
                        {
                            DisplayName = displayNameAttr.StringValue,
                            Username = accountNameAttr.StringValue,
                            Email = emailAttr.StringValue,
                            Roles = memberAttr.StringValueArray
                                .Select(x => GetGroup(x))
                                .Where(x => x != null)
                                .Distinct()
                                .ToArray()
                        };
                    }
                }
            }
            finally
            {
                _connection.Disconnect();
            }

            return null;
        }
    }
}

AccountController.cs

Luego, finalmente, después de verificar al usuario, debe construir el principal a partir de las reclamaciones del usuario para el proceso de inicio de sesión, lo que generaría la cookie detrás de la escena.

public class AccountController : Controller
{
    private readonly IAuthenticationService _authService;

    public AccountController(IAuthenticationService authService)
    {
        _authService = authService;
    }

    ...
    [HttpPost]
    [AllowAnonymous]
    public async Task<IActionResult> Login(LoginViewModel model)
    {
        if (ModelState.Valid)
        {
            try
            {
                var user = _authService.Login(model.Username, model.Password);
                if (user != null)
                {
                    var claims = new List<Claim>
                    {
                        new Claim(ClaimTypes.Name, user.Username),
                        new Claim(CustomClaimTypes.DisplayName, user.DisplayName),
                        new Claim(ClaimTypes.Email, user.Email)
                    }

                    // Roles
                    foreach (var role in user.Roles)
                    {
                        claims.Add(new Claim(ClaimTypes.Role, role));
                    }

                    // Construct Principal
                    var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, _authService.GetType().Name));

                    await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, 
                        principal,
                        new AuthenticationProperties
                        {
                            IsPersistent = model.RememberMe
                        }
                    );

                    return Redirect(Url.IsLocalUrl(model.ReturnUrl)
                        ? model.ReturnUrl
                        : "/");              
                }

                ModelState.AddModelError("", @"Your username or password is incorrect.");
            }
            catch(Exception ex)
            {
                ModelState.AddModelError("", ex.Message);
            }
        }
        return View(model);
    }
}
1
David Liang 20 oct. 2017 a las 18:21

¿Te ayudaría esta publicación a integrarte con AD para la autenticación y autorización? MVC Core Cómo forzar / establecer una autorización global para todas las acciones?

La idea es agregar autenticación dentro del método ConfigureServices en el archivo Startup.cs:

services.AddMvc(config =>
{
    var policy = new AuthorizationPolicyBuilder()
                     .RequireAuthenticatedUser()
                     .RequireRole([Your AD security group name in here without domain name]) // This line adds authorization to users in the AD group only
                     .Build();
    config.Filters.Add(new AuthorizeFilter(policy));
});
0
Xiao Han 14 may. 2018 a las 19:54

En Asp.Net Core, la autenticación se controla a través de las propiedades del proyecto.

Abre la solución. Haga clic derecho en el Proyecto y haga clic en Propiedades. Haz clic en la pestaña Depurar. Marque la casilla de verificación Habilitar autenticación de Windows. Asegúrese de que la autenticación anónima esté deshabilitada.

Aquí está el documento de Microsoft, https://docs.microsoft.com / es-es / aspnet / core / security / autenticación / windowsauth

¡Salud!

-1
Jared 20 oct. 2017 a las 17:05