我需要同时具有Windows身份验证和OWIN(表单)身份验证,但我无法正常工作。
最好的选择是拥有两个具有不同身份验证方法的站点。
我找到了一个可以执行我想要的项目:mvc5-mixedauth。但是它使用了iisexpress,我无法与本地IIS一起使用。
发生的错误是:
请求过滤在Web服务器上配置以拒绝请求,因为查询字符串太长。
如果我删除所有我的 configureAuth()方法 startup.auth.cs 它不会丢弃错误,但我无法登录,因为它需要它做 cookiuthentication 。
startup.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);
}
有什么想法吗?
更新1
错误
请求过滤在Web服务器上配置以拒绝请求,因为查询字符串太长。
出现,因为在尝试到达登录页面时发生登录循环。
解决!
我遵循以下示例:mvc5-mixauth
学分:穆罕默德·尤恩斯(Mohammed Younes)
更新1
问题:我需要同时具有匿名身份验证和 Windows Authentication 启用。但是,当您启用两者时,您只能获得 nt权威 iusr 。
分辨率:要获取当前用户(使用NTLM提示介绍),我们需要创建一个处理程序,该处理程序将在用户输入登录页面时执行。当用户访问登录页面时,处理程序将在浏览器中获得当前的Windows身份,然后将其设置为 logonuseridentity 。
。注意:我需要使用 Windows First 登录,当用户登录登录页面时,它将尝试获取通讯员ASP.NET用户。
处理程序
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
}
}
扩展
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);
}
}
}
注意您需要将accountController.cs作为部分。
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; }
}
}
然后,您需要添加处理程序:
<add name="Windows Login Handler" path="Login" verb="GET,POST" type="MixedAuth.WindowsLoginHandler" preCondition="integratedMode" />
startup.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);
然后,您需要配置本地IIS 以使用 WindowsAuthentication 和 anonymousauthentication 。您可以在身份验证模块中执行此操作。
note 如果没有 Windemauthentication 转到控制面板然后程序和功能然后"转Windows功能打开或关闭:
选择" Internet信息服务">"万维网">"安全" 然后选择Windows身份验证。
我在您的答案中没有看到这个,因此对于任何人在Owin Pipeline中捕获Windows auth的人,您还可以将以下内容添加到ConfigureAuth
方法:
public void ConfigureAuth(IAppBuilder app)
{
HttpListener listener = (HttpListener)app.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
}