使用FederatedAuthentication.SessionAuthenticationModule

时间:2015-07-29 04:13:23

标签: c# authentication session-cookies windows-authentication

对于基于MVC5的Intranet应用程序,我试图在声明感知主体的Windows身份之上实现基于Windows的身份验证和自定义声明。

一切都很顺利,直到我尝试从会话cookie中存储的SessionSecurityToken中读取身份时,出于某种原因,当我尝试将“安全句柄已关闭”错误时登陆我的主页/索引视图后转到任何其他视图。

以下是我到目前为止所得到的内容 -

  1. 在项目属性中 - Windows Auth = Enabled,Anonymous Auth = disabled。
  2. 作为.net 4.5上的MVC5项目,默认情况下该项目设计为使用OWIN(如果你在项目创建时没有选择Windows身份验证,则是VS这样做),我觉得OWIN不会盯着Windows Auth,所以我在开始时通过在startup.cs中注释'ConfigAuth'来禁用OWIN
  3. 写了一个类来自定义windows主体标识,并使用 FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie

    将sessionSecurityToken写入cookie
    public class CustomClaimsTransformer : ClaimsAuthenticationManager
    {
      public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
      {
        if (!incomingPrincipal.Identity.IsAuthenticated)
        {
            return base.Authenticate(resourceName, incomingPrincipal);
        }
    
        ClaimsPrincipal transformedPrincipal = CustomizePrincipal(ClaimsPrincipal.Current.Identities.First(), incomingPrincipal.Identity.Name);
        CreateSession(transformedPrincipal);
    
        return transformedPrincipal;
      }
    
    
       private void CreateSession(ClaimsPrincipal transformedPrincipal)
       {
           SessionSecurityToken sessionSecurityToken = new SessionSecurityToken(transformedPrincipal, TimeSpan.FromHours(12));
           FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionSecurityToken);
       }
    
       private ClaimsPrincipal CustomizePrincipal(ClaimsIdentity userClaimsIdentity, String userName)
       {
        List<Claim> claims = new List<Claim>();
    
        PrincipalContext princiContxt = null; ;
        UserPrincipal princi = null;
        ClaimsIdentity custClaimsIdentity = new ClaimsIdentity();
        princiContxt = new PrincipalContext(ContextType.Domain);
        princi = UserPrincipal.FindByIdentity(princiContxt, userName);//);
        userClaimsIdentity.AddClaims(new[] {
                new Claim("CustGroup", "CustTeam"),
                new Claim(ClaimTypes.Email, princi.EmailAddress),
                ... ///more claims added here 
            });
    
        return new ClaimsPrincipal(userClaimsIdentity); 
       }
    }
    
  4. 在Web.config中,我添加了以下内容:
    在'configSections'下:

  5.     <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
       <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
    

    在'system.identityModel'中:

    <system.identityModel>
        <identityConfiguration>
          <claimsAuthenticationManager type="myProject.CustomClaimsTransformer,myProject"/>
        </identityConfiguration>
     </system.identityModel>
    

    还在system.webserver中添加了以下模块:

    <modules>
        <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"></add>
    </modules>
    
    1. 接下来,我有自定义授权过滤器属性,以使用上面定义的CustomClaimsTransformer类来授权使用自定义声明:

      public class PROJClaimsAuthorizeAttribute : AuthorizeAttribute  {
          public string ClaimType { get; set; }
          public string ClaimValue { get; set; }
      
      //Called when access is denied
      protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
      {
          //User isn't logged in
          if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
          {
              filterContext.Result = new RedirectToRouteResult(
                      new RouteValueDictionary(new { controller = "Home", action = "Index" })
              );
          }
          //User is logged in but has no access
          else
          {
              filterContext.Result = new RedirectToRouteResult(
                      new RouteValueDictionary(new { controller = "Error", action = "AccessDenied" })
              );
          }
      }
      
      //Core authentication, called before each action
      protected override bool AuthorizeCore(HttpContextBase context)
      {
      
          SessionSecurityToken token;
          ClaimsIdentity claimsIdentity = null;
          ClaimsIdentity userClaimsIdentity = null;
          ClaimsPrincipal customClaimsPrinci = null;
          ClaimsAuthenticationManager authManager = null;
      
          var isAuthorized  = false;
          try
          {
              claimsIdentity = context.User.Identity as ClaimsIdentity; // get current user's ClaimsIdentity (Widnow's identity as  ClaimsIdentity)
              isAuthorized = base.AuthorizeCore(context);
              if (!context.Request.IsAuthenticated || !isAuthorized)
              {
                  return false;
              }
      
              ///////******* THE Error Causing IF statement *******************************************
              //check if the SessionSecurityToken is available in cookie
              if (FederatedAuthentication.SessionAuthenticationModule.TryReadSessionTokenFromCookie(out token))
              {
                  //var accessToken = await tokenManager.GetTokenFromStoreAsync(token.ClaimsPrincipal.Identity.Name);
                  claimsIdentity = token.ClaimsPrincipal.Identity as ClaimsIdentity;
              }
              else
              {
                  //else get the principal with Custom claims identity using CustomClaimsTransformer, which also sets it in cookie
                  ClaimsPrincipal currentPrincipal = ClaimsPrincipal.Current;
                  CustomClaimsTransformer customClaimsTransformer = new CustomClaimsTransformer();
                  ClaimsPrincipal tranformedClaimsPrincipal = customClaimsTransformer.Authenticate(string.Empty, currentPrincipal);
                  Thread.CurrentPrincipal = tranformedClaimsPrincipal;
                  HttpContext.Current.User = tranformedClaimsPrincipal;
              }
      
      
              isAuthorized = checkClaimValidity(claimsIdentity, ClaimType, ClaimValue);
          }
          catch (Exception e)
          {
              // Error handling code
             var exptnMsg = "error setting AuthorizeCore" + e.Message;
             return false;
          }
      
          return isAuthorized;
      } // </ protected override bool AuthorizeCore >
      
      
      //checks Claim type/value in the given Claims Identity
      private Boolean checkClaimValidity(ClaimsIdentity pClaimsIdentity, string pClaimType, string pClaimValue)
      {
          Boolean blnClaimsValiditiy = false;
          //now check the passed in Claimtype has the passed in Claimvalue
          if (pClaimType != null && pClaimValue != null)
          {
              if ((pClaimsIdentity.HasClaim(x => x.Type.ToLower() == pClaimType.ToLower() && x.Value.ToLower() == pClaimValue.ToLower())))
              {
                  blnClaimsValiditiy = true;
              }
          }
      
          return blnClaimsValiditiy;
       }
      }
      
    2. 在此之后,我可以使用我的自定义Authorize属性'PROJClaimsAuthorizeAttribute'来装饰我的控制器类,如下所示:

    3. [PROJClaimsAuthorizeAttribute(ClaimType = "CustGroup", ClaimValue = "CustTeam")]
             public class HomeController : Controller
             {
                  public ActionResult Index()
                  {
                      return View();
                  }
              }
      

      这一切都很好。问题是 - 从索引视图,如果我尝试导航到其他视图 - 我得到'安全句柄已关闭'错误。 (当我删除标记为'错误导致IF语句******上面的if语句的'if'部分时,只保留其他部分,然后它运行良好,但后来我没有使用sessionSecurityToken cookie)。

      过去几天我一直在摸索这个错误,搜索谷歌/ SO等,但到目前为止没有运气。 所以最后想到把这个扔给SO专家社区,如果有人能够解释问题的原因和位置,我们将非常感激。真诚地感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

我修复了它 - this SO post给了一个线索。创建新身份并向此身份添加自定义声明,而不是自定义现有的Windows主要声明身份,有助于摆脱安全句柄已关闭&#39;错误。

在CustomClaimsTransformer类中 - CustomClaimsTransformer.CustomizePrincipal

/* commented this earlier code of adding claims to userClaimsIdentity
userClaimsIdentity.AddClaims(new[] {
        new Claim("CustGroup", "CustTeam"),
        new Claim(ClaimTypes.Email, princi.EmailAddress),
        ... ///more claims added here 
    });
    return new ClaimsPrincipal(userClaimsIdentity); 
 */

并替换为创建新的ClaimsIdentity的代码,如下所示

        List<Claim> newClaims = new List<Claim>();
        newClaims.Add(new Claim("CustGroup ", "CustTeam"));
        newClaims.Add(new Claim(ClaimTypes.Email, princi.EmailAddress));
        ... ///more claims added here 

           ClaimsIdentity ci = new ClaimsIdentity(newClaims, "CustomClaims");
        return new ClaimsPrincipal(ci); //userClaimsIdentity); 

现在它的工作正常,用户身份验证/身份/声明将保存在SessionTokenCookie中