【问题标题】:'safe handle has been closed' error when using FederatedAuthentication.SessionAuthenticationModule使用 FederatedAuthentication.SessionAuthenticationModule 时出现“安全句柄已关闭”错误
【发布时间】:2015-10-19 21:10:38
【问题描述】:

对于基于 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”下:

    <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. 接下来,我有自定义 Authorize 过滤器属性来使用上面定义的 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. 之后,我可以使用我的自定义授权属性“PROJClaimsAuthorizeAttribute”来装饰我的控制器类,如下所示:

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

一切正常。问题是 - 从索引视图中,如果我尝试导航到其他视图 - 我会收到“安全句柄已关闭”错误。 (当我删除上面标有 'THE Error Causing IF statement****** 的 if 语句的 'if' 部分,并保留 else 部分中的任何内容时,它工作得很好,但是我没有使用sessionSecurityToken cookie)。

在过去的几天里,我一直在摸索以找出这个错误,搜索了 google/SO 等,但到目前为止还没有运气。 所以最后想到在这里把这个扔给 SO 专家社区,如果有人对问题可能在哪里/在哪里有所了解,我将不胜感激。提前真诚感谢您的帮助。

【问题讨论】:

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


    【解决方案1】:

    我把它修好了——this SO post 给出了一个线索。无需自定义现有的 Windows 主体 claimIdentity,而是创建一个新标识并向该标识添加自定义声明有助于消除“安全句柄已关闭”错误。

    在 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 中

    【讨论】:

      猜你喜欢
      • 2021-11-02
      • 1970-01-01
      • 2014-01-09
      • 2014-01-17
      • 2014-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-22
      相关资源
      最近更新 更多