0

我遇到了一個奇怪的問題,我的mvc 5 web應用程序似乎無法解決。該應用程序是我爲我的組織構建的內部Web應用程序。主要是由使用連接域的計算機的員工訪問,但也可以通過移動設備和平板電腦訪問。我想通過windows auth和AD提供前者的自動登錄體驗(如果他們已經登錄到域中,則不必輸入憑據),我還希望能夠向所有其他用戶提供自定義登錄屏幕,而不是瀏覽器本機提示。爲了實現這個功能,我創建了一個單獨的web應用程序,它對windows用戶進行身份驗證,並將加密的cookie發送回具有用戶角色的主應用程序。基於非Windows的瀏覽器在主應用程序中顯示一個登錄頁面,用於對AD進行身份驗證並檢索用戶的角色。對於每種登錄類型的角色,都要轉換爲聲明,併爲用戶創建聯合令牌。 我的問題是,當用戶通過重定向到Windows身份驗證應用程序登錄時出現一個奇怪的問題。我提交的任何表單,無論是標準表單提交還是AJAX帖子,都必須在加載頁面的一分鐘內提交,否則發送給控制器操作的參數不會綁定(null)。如果用戶通過自定義登錄頁面登錄,則此問題不存在。ASP.NET MVC操作參數在表單閒置時無法綁定

以下是處理在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,並在重定向回主應用後對代碼和令牌創建進行硬編碼。每次點擊Application_AuthenticateRequest時,HttpContext.Current.User對象將保持設置爲有效的claimsPrincipal。我已經實現了一個自定義AuthorizeAttribute,並且用戶總是被認證和授權。有趣的是,如果我在參數傳遞爲null後立即再次敲擊窗體上的提交按鈕,它就會起作用。我在網上搜索了一個類似的問題 - 沒有 - 我希望有人在這裏有一個想法。

回答

0

我的答案可能是完全無關的,但我只是在幫助。我們有類似的東西。當我們從MVC4的web api切換到MVC5時。我們有一個AuthorizationAttribute檢查令牌。我們閱讀它,設置委託人,但是當我們觸及控制器操作時,委託人已經不在了(在mVC5中 - 在以前的版本中工作)。簡言之,它與MVC5的異步本質有關。我們改變了實現另一個實現了IAuthenticationFilter的屬性(這是mVC5期望的主體被設置的地方)。 這與你有什麼關係?我認爲你也有異步問題,並且你需要找出MVC5 預計你設置主體(Application_AuthenticateRequest 可能不是地方)。任何其他的東西都可能設置在錯誤的線程上,當你到達控制器時會消失。

+1

感謝Willy的想法,但是當您使用會話身份驗證令牌時,您無需爲每個請求設置主體。一旦用戶登錄並進行身份驗證,會話標記就會以聲明標識/委託人的身份爲其創建,並且它將保持設置狀態,直到該標記變爲無效或它們退出。爲了確保我檢查了控制器操作,並且仍然設置了用戶身份,但發送給操作的參數爲空。 – PlasmaX