【发布时间】:2017-07-16 09:59:58
【问题描述】:
我的 mvc 5 Web 应用程序遇到一个奇怪的问题,我似乎无法弄清楚。该应用程序是我为我的组织构建的内部 Web 应用程序。它主要由使用域连接计算机的员工访问,但也将通过移动设备和平板电脑访问。我想通过 windows auth 和 AD 为前者提供自动登录体验(如果他们已经登录到域,则无需输入凭据)我还希望能够为所有其他用户提供自定义登录屏幕,而不是浏览器原生提示。为了实现这一点,我创建了一个单独的 Web 应用程序,它对 Windows 用户进行身份验证并将加密的 cookie 发送回具有用户角色的主应用程序。非基于 Windows 的浏览器在主应用程序中显示一个登录页面,该页面针对 AD 进行身份验证并检索用户的角色。对于每种类型的登录,角色都将转换为声明,并为用户创建联合令牌。 我的问题是,当用户通过重定向登录到 windows auth 应用程序时,会出现一个奇怪的问题。我提交的任何表单(无论是标准表单提交还是 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。我已经实现了一个自定义 AuthorizeAttribute,并且用户总是经过身份验证和授权。有趣的是,如果我在参数以 null 传递后立即再次点击表单上的提交按钮,它就可以工作。我在网上搜索了一个类似的问题 - 没有 - 我希望这里有人有一个想法。
【问题讨论】:
标签: asp.net-mvc authentication asp.net-mvc-5 wif claims-based-identity