我的mvc 5网络应用程序出现了一个奇怪的问题,我似乎无法弄明白。该应用程序是我为我的组织构建的内部Web应用程序。主要是由使用域连接计算机的员工访问,但也可以通过移动设备和平板电脑访问。我想通过Windows身份验证和AD提供前者自动登录体验(如果他们已经登录到域,则无需输入凭据)我还希望能够为所有其他用户提供自定义登录屏幕而不是浏览器原生提示。为了实现这一点,我创建了一个单独的Web应用程序,该应用程序对Windows用户进行身份验证,并使用用户的角色将加密的cookie发送回主应用程序。基于非Windows的浏览器在主应用程序中显示登录页面,该登录页面针对AD进行身份验证并检索用户的角色。对于每种类型的登录,角色将转换为声明,并为用户创建联合令牌。 我的问题是,当用户通过重定向登录到Windows身份验证应用程序时,会出现一个奇怪的问题。我提交的任何表单,无论是标准表单提交还是AJAX帖子,都必须在加载页面的一分钟内提交,否则发送到控制器操作的参数不会绑定(null)。如果用户通过自定义登录页面登录,则此问题不存在。
以下是在global.asax中执行初始身份验证的代码:
Protected Sub Application_AuthenticateRequest()
Dim user As System.Security.Principal.IPrincipal = HttpContext.Current.User
If user Is Nothing Then
'First check if an authentication cookie is has been generated from the windows login
'authentication app
Dim authCookie As HttpCookie = Request.Cookies(".ConnectAUTH")
If Not authCookie Is Nothing Then
' Extract the roles from the cookie, and assign to our current principal, which is attached to the HttpContext.
Dim ticket As FormsAuthenticationTicket = FormsAuthentication.Decrypt(authCookie.Value)
Dim claims As New List(Of Claim)
For Each role In ticket.UserData.Split(";"c)
claims.Add(New Claim(ClaimTypes.Role, role))
Next
Dim claimIdent As New ClaimsIdentity(claims, "Custom")
claimIdent.AddClaim(New Claim(ClaimTypes.WindowsAccountName, ticket.Name))
claimIdent.AddClaim(New Claim(ClaimTypes.NameIdentifier, ticket.Name))
Dim claimPrinc As New ClaimsPrincipal(claimIdent)
Dim token = New SessionSecurityToken(claimPrinc)
Dim sam = FederatedAuthentication.SessionAuthenticationModule
sam.WriteSessionTokenToCookie(token)
HttpContext.Current.User = New ClaimsPrincipal(claimIdent)
Return
Else 'User hasn't been authenticated
Dim ConnectBaseURL = Request.Url.GetLeftPart(UriPartial.Authority) & "/"
Dim mvcPath As String = Request.Url.ToString.Replace(ConnectBaseURL, "")
'If user is requesting the login page then let them authenticate through there
If mvcPath.ToUpper.Contains("ACCOUNT/LOGIN") Or mvcPath.ToUpper.Contains("ACCOUNT/LOGUSERIN") Or mvcPath.ToUpper.Contains("SIGNALR") Then Exit Sub
'for brevity i will omit the code below:
' Basically it checks whether the browser is windows based if so then it redirects the user to
' a windows login authenticator which authenticates the user against Active Directory, builds and sends
' an encrypted cookie with a list of roles/groups that the user belongs to. When this function detects that cookie
' it decrypts it, sets up a claims identity, add the user roles and creates a federated authentication token
'If the the browser is not windows based then it redirects the user to a custom login page which is tied to an MVC
'action that will also authenticate the user against active directory and and set up the claims identity ...
End If
End If
End Sub
以下是用户在重定向到自定义登录页面时对用户进行身份验证的代码:
<AllowAnonymous>
<HttpPost>
<ValidateAntiForgeryToken>
<OutputCache(NoStore:=True, Duration:=0, Location:=OutputCacheLocation.None, VaryByParam:="None")>
Public Function LogUserIn(model As User, returnURL As String) As ActionResult
Try
If ModelState.IsValid Then
If Membership.ValidateUser(model.UserName, model.PassWord) Then
'Call helper function to get all user roles and convert them to claims
Dim userClaims As List(Of Claim) = New LDAPHelper().GetUserGroups(model.UserName)
userClaims.Add(New Claim(ClaimTypes.WindowsAccountName, model.UserName))
userClaims.Add(New Claim(ClaimTypes.NameIdentifier, model.UserName))
userClaims.Add(New Claim(ClaimTypes.Name, model.UserName))
Dim claimIdent As New ClaimsIdentity(userClaims, "Custom")
Dim claimPrinc As New ClaimsPrincipal(claimIdent)
Dim token = New SessionSecurityToken(claimPrinc)
Dim sam = FederatedAuthentication.SessionAuthenticationModule
sam.WriteSessionTokenToCookie(token)
If returnURL Is Nothing Then
Return Redirect("~/")
Else
Return Redirect(returnURL)
End If
Else
ModelState.AddModelError("LoginFailure", "The username/password combination was invalid")
End If
End If
Return Nothing
Catch ex As Exception
ModelState.AddModelError("LoginFailure", ex.Message)
Return Nothing
End Try
End Function
我尝试通过不从windows auth应用程序发送表单cookie来消除表单cookie,只是在重定向回主应用程序后硬编码声明和令牌创建。每次命中Application_AuthenticateRequest时,HttpContext.Current.User对象都会保持设置为有效的claimPrincipal。我已经实现了Custom AuthorizeAttribute,并且用户始终经过身份验证和授权。有趣的是,如果我在参数传递后立即再次点击表单上的提交按钮为null,它就可以工作。我在网上搜索了一个类似的问题 - 没什么 - 我希望有人在这里有个主意。
答案 0 :(得分:0)
我的回答可能完全不相关,但我只是想提供帮助。我们有类似的东西。当我们从MVC4中的web api切换到MVC5时。我们有一个AuthorizationAttribute来检查令牌。我们读了它,设置了主体,但是当我们点击控制器动作时,主体就消失了(在mVC5中 - 在之前的版本中这是有效的)。简短的故事是它与MVC5的异步性质有关。我们改变了实现IAuthenticationFilter的另一个属性的方式(这是mVC5期望设置主体的地方)。 这与你有什么关系?我认为你也遇到异步问题,你需要弄清楚MVC5 期望你在哪个事件中设置主体(Application_AuthenticateRequest 可能不是这个地方)。其他任何东西都可能设置在错误的线程上,当你到达控制器时就会消失。