Saltar al contenido

MVC – Autenticación mixta – OWIN + Autenticación de Windows

Ya no tienes que investigar más por todo internet ya que has llegado al sitio adecuado, poseemos la solución que buscas sin complicarte.

Solución:

¡Resuelto!

Seguí el ejemplo: MVC5-MixAuth

Créditos: Mohammed Younes

ACTUALIZACIÓN 1

Problema: Necesitaba tener ambos Autenticación anónima y Autenticación de Windows activado. Pero cuando los habilita a ambos, solo puede obtener AUTORIDAD DEL NT IUSR.

Resolución: Para obtener el usuario actual (introducido con el indicador NTLM), necesitamos crear un controlador que se ejecutará cuando un usuario ingrese a la página de inicio de sesión. Cuando el usuario accede a la página de inicio de sesión, el controlador obtendrá la identidad de Windows actual almacenada en caché en el navegador y luego se configurará como el LogonUserIdentity.

Nota: Necesitaba usar ventanas primero inicio de sesión, cuando el usuario ingresa a la página de inicio de sesión, intentará obtener el usuario ASP.NET correspondiente.

Manipulador

using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.AspNet.Identity;

namespace MixedAuth


    /// 
    /// Managed handler for windows authentication.
    /// 
    public class WindowsLoginHandler : HttpTaskAsyncHandler, System.Web.SessionState.IRequiresSessionState
    
        public HttpContext Context  get; set; 
        public override async Task ProcessRequestAsync(HttpContext context)
        
            this.Context = context;

            //if user is already authenticated, LogonUserIdentity will be holding the current application pool identity.
            //to overcome this:
            //1. save userId to session.
            //2. log user off.
            //3. request challenge.
            //4. log user in.

            if (context.User.Identity.IsAuthenticated)
            
                this.SaveUserIdToSession(context.User.Identity.GetUserId());

                await WinLogoffAsync(context);

                context.RequestChallenge();
            
            else if (!context.Request.LogonUserIdentity.IsAuthenticated)
            
                context.RequestChallenge();
            
            else
            
                // true: user is trying to link windows login to an existing account
                if (this.SessionHasUserId())
                
                    var userId = this.ReadUserIdFromSession();
                    this.SaveUserIdToContext(userId);
                    await WinLinkLoginAsync(context);
                
                else // normal login.
                    await WinLoginAsync(context);
            
        

        #region helpers
        /// 
        /// Executes Windows login action against account controller.
        /// 
        /// 
        /// 
        private async Task WinLoginAsync(HttpContext context)
        
            var routeData = this.CreateRouteData(Action.Login);

            routeData.Values.Add("returnUrl", context.Request["returnUrl"]);
            routeData.Values.Add("userName", context.Request.Form["UserName"]);

            await ExecuteController(context, routeData);
        

        /// 
        /// Execute Link Windows login action against account controller.
        /// 
        /// 
        /// 
        private async Task WinLinkLoginAsync(HttpContext context)
        
            var routeData = this.CreateRouteData(Action.Link);

            await ExecuteController(context, routeData);
        

        /// 
        /// Executes Windows logoff action against controller.
        /// 
        /// 
        /// 
        private async Task WinLogoffAsync(HttpContext context)
        
            var routeData = this.CreateRouteData(Action.Logoff);

            await ExecuteController(context, routeData);
        

        /// 
        /// Executes controller based on route data.
        /// 
        /// 
        /// 
        /// 
        private async Task ExecuteController(HttpContext context, RouteData routeData)
        
            var wrapper = new HttpContextWrapper(context);
            MvcHandler handler = new MvcHandler(new RequestContext(wrapper, routeData));

            IHttpAsyncHandler asyncHandler = ((IHttpAsyncHandler)handler);
            await Task.Factory.FromAsync(asyncHandler.BeginProcessRequest, asyncHandler.EndProcessRequest, context, null);
        

        #endregion
    

Extensiones

using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;

namespace MixedAuth

    public enum Action  Login, Link, Logoff ;

    public static class MixedAuthExtensions
    
        const string userIdKey = "windows.userId";
        //http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
        const int fakeStatusCode = 418;

        const string controllerName = "Account";
        const string loginActionName = "WindowsLogin";
        const string linkActionName = "LinkWindowsLogin";
        const string logoffActionName = "WindowsLogoff";
        const string windowsLoginRouteName = "Windows/Login";


        public static void RegisterWindowsAuthentication(this MvcApplication app)
        
            app.EndRequest += (object sender, EventArgs e) =>
            
                HttpContext.Current.ApplyChallenge();
            ;
        

        /// 
        /// Registers ignore route for the managed handler.
        /// 
        /// 
        public static void IgnoreWindowsLoginRoute(this RouteCollection routes)
        
            routes.IgnoreRoute(windowsLoginRouteName);
        

        /// 
        /// By pass all middleware and modules, by setting a fake status code.
        /// 
        /// 
        public static void RequestChallenge(this HttpContext context)
        
            context.Response.StatusCode = fakeStatusCode;
        

        /// 
        /// Invoke on end response only. Replaces the current response status code with 401.2
        /// 
        /// 
        public static void ApplyChallenge(this HttpContext context)
        
            if (context.Response.StatusCode == fakeStatusCode)
            
                context.Response.StatusCode = 401;
                context.Response.SubStatusCode = 2;

                //http://msdn.microsoft.com/en-us/library/system.web.httpresponse.tryskipiiscustomerrors(v=vs.110).aspx
                //context.Response.TrySkipIisCustomErrors = true;
            
        

        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static RouteData CreateRouteData(this WindowsLoginHandler handler, Action action)
        
            RouteData routeData = new RouteData();
            routeData.RouteHandler = new MvcRouteHandler();

            switch (action)
            
                case Action.Login:
                    routeData.Values.Add("controller", controllerName);
                    routeData.Values.Add("action", loginActionName);
                    break;
                case Action.Link:
                    routeData.Values.Add("controller", controllerName);
                    routeData.Values.Add("action", linkActionName);
                    break;
                case Action.Logoff:
                    routeData.Values.Add("controller", controllerName);
                    routeData.Values.Add("action", logoffActionName);
                    break;
                default:
                    throw new NotSupportedException(string.Format("unknonw action value '0'.", action));
            
            return routeData;
        


        /// 
        /// Saves userId to the items collection inside .
        /// 
        public static void SaveUserIdToContext(this WindowsLoginHandler handler, string userId)
        
            if (handler.Context.Items.Contains(userIdKey))
                throw new ApplicationException("Id already exists in context.");

            handler.Context.Items.Add("windows.userId", userId);
        

        /// 
        /// Reads userId from item collection inside .
        /// 
        /// The item will removed before this method returns
        /// 
        /// 
        public static int ReadUserId(this HttpContextBase context)
        
            if (!context.Items.Contains(userIdKey))
                throw new ApplicationException("Id not found in context.");

            int userId = Convert.ToInt32(context.Items[userIdKey] as string);
            context.Items.Remove(userIdKey);

            return userId;
        

        /// 
        /// Returns true if the session contains an entry for userId.
        /// 
        public static bool SessionHasUserId(this WindowsLoginHandler handler)
        
            return handler.Context.Session[userIdKey] != null;
        

        /// 
        /// Save a session-state value with the specified userId.
        /// 
        public static void SaveUserIdToSession(this WindowsLoginHandler handler, string userId)
        
            if (handler.SessionHasUserId())
                throw new ApplicationException("Id already exists in session.");

            handler.Context.Session[userIdKey] = userId;
        

        /// 
        /// Reads userId value from session-state.
        /// 
        /// The session-state value removed before this method returns.
        /// 
        /// 
        public static string ReadUserIdFromSession(this WindowsLoginHandler handler)
        
            string userId = handler.Context.Session[userIdKey] as string;

            if (string.IsNullOrEmpty(userIdKey))
                throw new ApplicationException("Id not found in session.");

            handler.Context.Session.Remove(userIdKey);

            return userId;
        


        /// 
        /// Creates a form for windows login, simulating external login providers.
        /// 
        /// 
        /// 
        /// 
        public static MvcForm BeginWindowsAuthForm(this HtmlHelper htmlHelper, object htmlAttributes)
        
            return htmlHelper.BeginForm("Login", "Windows", FormMethod.Post, htmlAttributes);
        

        /// 
        /// Creates a form for windows login, simulating external login providers.
        /// 
        /// 
        /// 
        /// 
        public static MvcForm BeginWindowsAuthForm(this HtmlHelper htmlHelper, object routeValues, object htmlAttributes)
        
            return htmlHelper.BeginForm("Login", "Windows", FormMethod.Post, htmlAttributes);
        



    

Nota
Necesita tener AccountController.cs como parcial.

AccountController.Windows.cs

using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.AspNet.Identity;
using MixedAuth;

namespace EmployeePortal.Web.Controllers

    [Authorize]
    public partial class AccountController : BaseController
     HttpVerbs.Post)]
        public ActionResult WindowsLogin(string userName, string returnUrl)
        
            if (!Request.LogonUserIdentity.IsAuthenticated)
            
                return RedirectToAction("Login");
            

            var loginInfo = GetWindowsLoginInfo();

            // Sign in the user with this external login provider if the user already has a login
            var user = UserManager.Find(loginInfo);
            if (user != null)
            
                SignIn(user, isPersistent: false);
                return RedirectToLocal(returnUrl);
            
            else
            
                return RedirectToAction("Login", new RouteValueDictionary(new  controller = "Account", action = "Login", returnUrl = returnUrl ));
            
        

        //
        // POST: /Account/WindowsLogOff
        [HttpPost]
        [ValidateAntiForgeryToken]
        public void WindowsLogOff()
        
            AuthenticationManager.SignOut();
        

        //
        // POST: /Account/LinkWindowsLogin
        [AllowAnonymous]
        [HttpPost]
        public async Task LinkWindowsLogin()
        
            int userId = HttpContext.ReadUserId();

            //didn't get here through handler
            if (userId <= 0)
                return RedirectToAction("Login");

            HttpContext.Items.Remove("windows.userId");

            //not authenticated.
            var loginInfo = GetWindowsLoginInfo();
            if (loginInfo == null)
                return RedirectToAction("Manage");

            //add linked login
            var result = await UserManager.AddLoginAsync(userId, loginInfo);

            //sign the user back in.
            var user = await UserManager.FindByIdAsync(userId);
            if (user != null)
                await SignInAsync(user, false);

            if (result.Succeeded)
                return RedirectToAction("Manage");

            return RedirectToAction("Manage", new  Message = ManageMessageId.Error );
        

        #region helpers
        private UserLoginInfo GetWindowsLoginInfo()
        
            if (!Request.LogonUserIdentity.IsAuthenticated)
                return null;

            return new UserLoginInfo("Windows", Request.LogonUserIdentity.User.ToString());
        
        #endregion
    

    public class WindowsLoginConfirmationViewModel
    
        [Required]
        [Display(Name = "User name")]
        public string UserName  get; set; 
    

Luego, debe agregar el controlador:


Startup.cs

app.CreatePerOwinContext(dbEmployeePortal.Create);
app.CreatePerOwinContext(ApplicationUserManager.Create);
app.CreatePerOwinContext(ApplicationSignInManager.Create);


PathString path = new PathString("/Account/Login");
if (GlobalExtensions.WindowsAuthActive)
    path = new PathString("/Windows/Login");

app.UseCookieAuthentication(new CookieAuthenticationOptions

    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    //LoginPath = new PathString("/Account/Login")
    LoginPath = path
);
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

Entonces necesitas configurar IIS local usar Autenticación de Windows y Autenticación anónima. Puede hacer esto en el Módulo de autenticación.

Nota Si no tienes Autenticación de Windows ir a Panel de control luego Programas y características luego "Activar o desactivar las funciones de Windows":

seleccione "Servicios de información de Internet"> "World Wide Web"> "Seguridad" y seleccione Autenticación de Windows.

No vi esto en su respuesta, por lo que para cualquiera que busque cómo capturar Windows Auth en su canalización Owin, también puede agregar lo siguiente a su ConfigureAuth método:

public void ConfigureAuth(IAppBuilder app)

     HttpListener listener = (HttpListener)app.Properties["System.Net.HttpListener"];
     listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;

Reseñas y valoraciones

Recuerda que tienes la opción de agregar una reseña .

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *