MVC-Mixed Auth-OWIN + Windows Auth

j'ai besoin d'avoir à la fois l'authentification windows et owin (formulaires), l'authentification, mais je ne peux pas le faire fonctionner.

Probablement la meilleure option est d'avoir deux sites qui ont différentes méthodes d'authentification.

j'ai trouvé un projet qui fait ce que je veux: MVC5-MixedAuth. Mais il utilise IISExpress et je n'arrive pas à le faire fonctionner avec des IIS locaux.

L'erreur qui se produit est:

le filtrage des requêtes est configuré sur le serveur Web pour rejeter la demande parce que la chaîne de requête est trop longue.

Si je supprime tous mes ConfigureAuth () méthode à l'intérieur d' Démarrage.Auth.cs il ne jette pas l'erreur, mais je ne peux pas me connecter car il est nécessaire de faire CookieAuthentication.

Démarrage.Auth.cs:

public void ConfigureAuth(IAppBuilder app)
{
    app.CreatePerOwinContext(dbEmployeePortal.Create);
    app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
    app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/Account/Login"),
        Provider = new CookieAuthenticationProvider
        {
            OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, UserMaster, int>
            (
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
                    getUserIdCallback: (id) => (Int32.Parse(id.GetUserId()))
            )
        }
    });

    app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
    app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
    app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}

une idée?

UPDATE 1

L'erreur

le filtrage des requêtes est configuré sur le serveur Web pour rejeter la demande parce que la chaîne de requête est trop longue.

apparaît parce que se produit une boucle de connexion quand il tente d'atteindre la page de connexion.

19
demandé sur Leandro Soares 2015-05-07 18:40:35

2 réponses

Résolu!

j'ai suivi l'exemple: MVC5-MixAuth

Crédits: Mohamed Younes

UPDATE 1

Problème: j'ai besoin d'avoir les deux Authentification Anonyme et Authentification Windows activé. Mais quand vous les activez tous les deux, vous pouvez seulement obtenir NT AUTORITÉ\IUSR.

résolution: pour obtenir le utilisateur actuel (introduit avec L'invite NTLM), nous avons besoin de créer un gestionnaire qui s'exécute lorsqu'un utilisateur entre à la page de connexion. Lorsque l'utilisateur accède à la page d'ouverture de session, le gestionnaire obtiendra l'identité actuelle de windows mise en cache dans le navigateur, puis définie comme LogonUserIdentity.

Remarque: j'ai besoin d'utiliser windows login, lorsque l'utilisateur accède à la page de login, il va essayer d'obtenir le correspondant ASP.NET Utilisateur.

Gestionnaire

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

namespace MixedAuth
{

    /// <summary>
    /// Managed handler for windows authentication.
    /// </summary>
    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
        /// <summary>
        /// Executes Windows login action against account controller.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        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);
        }

        /// <summary>
        /// Execute Link Windows login action against account controller.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private async Task WinLinkLoginAsync(HttpContext context)
        {
            var routeData = this.CreateRouteData(Action.Link);

            await ExecuteController(context, routeData);
        }

        /// <summary>
        /// Executes Windows logoff action against controller.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private async Task WinLogoffAsync(HttpContext context)
        {
            var routeData = this.CreateRouteData(Action.Logoff);

            await ExecuteController(context, routeData);
        }

        /// <summary>
        /// Executes controller based on route data.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="routeData"></param>
        /// <returns></returns>
        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
    }
}

Extensions

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();
            };
        }

        /// <summary>
        /// Registers ignore route for the managed handler.
        /// </summary>
        /// <param name="routes"></param>
        public static void IgnoreWindowsLoginRoute(this RouteCollection routes)
        {
            routes.IgnoreRoute(windowsLoginRouteName);
        }

        /// <summary>
        /// By pass all middleware and modules, by setting a fake status code.
        /// </summary>
        /// <param name="context"></param>
        public static void RequestChallenge(this HttpContext context)
        {
            context.Response.StatusCode = fakeStatusCode;
        }

        /// <summary>
        /// Invoke on end response only. Replaces the current response status code with 401.2
        /// </summary>
        /// <param name="context"></param>
        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;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="handler"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        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;
        }


        /// <summary>
        /// Saves userId to the items collection inside <see cref="HttpContext"/>.
        /// </summary>
        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);
        }

        /// <summary>
        /// Reads userId from item collection inside <see cref="HttpContext"/>.
        /// </summary>
        /// <remarks>The item will removed before this method returns</remarks>
        /// <param name="context"></param>
        /// <returns></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;
        }

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

        /// <summary>
        /// Save a session-state value with the specified userId.
        /// </summary>
        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;
        }

        /// <summary>
        /// Reads userId value from session-state.
        /// </summary>
        /// <remarks>The session-state value removed before this method returns.</remarks>
        /// <param name="session"></param>
        /// <returns></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;
        }


        /// <summary>
        /// Creates a form for windows login, simulating external login providers.
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="htmlAttributes"></param>
        /// <returns></returns>
        public static MvcForm BeginWindowsAuthForm(this HtmlHelper htmlHelper, object htmlAttributes)
        {
            return htmlHelper.BeginForm("Login", "Windows", FormMethod.Post, htmlAttributes);
        }

        /// <summary>
        /// Creates a form for windows login, simulating external login providers.
        /// </summary>
        /// <param name="htmlHelper"></param>
        /// <param name="htmlAttributes"></param>
        /// <returns></returns>
        public static MvcForm BeginWindowsAuthForm(this HtmlHelper htmlHelper, object routeValues, object htmlAttributes)
        {
            return htmlHelper.BeginForm("Login", "Windows", FormMethod.Post, htmlAttributes);
        }



    }
}

Remarque: Vous devez avoir AccountController.cs partielle.

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
    {
        //
        // POST: /Account/WindowsLogin
        [AllowAnonymous]
        [AcceptVerbs(HttpVerbs.Get | 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<ActionResult> 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; }
    }
}

Ensuite, vous devez ajouter le gestionnaire de:

<add name="Windows Login Handler" path="Login" verb="GET,POST" type="MixedAuth.WindowsLoginHandler" preCondition="integratedMode" />

Démarrage.cs

app.CreatePerOwinContext(dbEmployeePortal.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(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);

alors vous devez configurer Local IIS utiliser WindowsAuthentication et AnonymousAuthentication. Vous pouvez le faire dans le Module d'Authentification.

Remarque: Si vous n'avez pas WindowsAuthentication Panneau De ContrôleProgrammes et Fonctionnalités puis "Activer ou désactiver les fonctions Windows":

sélectionner "Internet Information Services" > "World Wide Web" > " Security" et choisir Windows Authentication.

13
répondu Leandro Soares 2015-10-26 11:25:28

Je n'ai pas vu cela dans votre réponse, donc pour quiconque cherchant comment capturer Windows Auth dans votre pipeline Owin, vous pouvez aussi ajouter ce qui suit à votre ConfigureAuth méthode:

public void ConfigureAuth(IAppBuilder app)
{
     HttpListener listener = (HttpListener)app.Properties["System.Net.HttpListener"];
     listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
}
1
répondu codeMonkey 2017-03-21 18:36:15