根据声明允许或拒绝访问整个站点

时间:2012-10-31 12:33:38

标签: asp.net wif claims-based-identity fubumvc

我有一个FubuMvc网站,该网站使用来自WIF单点登录服务器的基于声明的授权。身份验证发生在SSO上,包括角色和一组自定义声明在内的声明将传递到网站进行授权。

SSO和网站与基于角色的授权一样正常,但我想要做的是拒绝访问整个网站,根据缺少自定义声明保存错误页面。目前,我使用的是自定义ClaimsAuthenticationManager,用于检查索赔并检查是否存在所需索赔。如果声明丢失,则会抛出异常。这会导致500错误,但我真正想要的是系统抛出401错误并重定向到网站上的Not Authorized页面。

以下是自定义ClaimsAuthenticationManager的示例。

public class CustomAuthenticationManager : ClaimsAuthenticationManager
{
    private readonly string expectedClaim;

    public CustomAuthenticationManager (object config)
    {
        var nodes = config as XmlNodeList;
        foreach (XmlNode node in nodes)
        {
            using (var stringReader = new StringReader(node.OuterXml))
            using (var rdr = new XmlTextReader(stringReader))
            {
                rdr.MoveToContent();
                rdr.Read();
                string claimType = rdr.GetAttribute("claimType");
                if (claimType.CompareTo(ClaimTypes.CustomClaim) != 0)
                {
                    throw new NotSupportedException("Only custom claims are supported");
                }
                expectedSystemName = rdr.GetAttribute("customClaimValue");
            }
        }
    }

    public override IClaimsPrincipal Authenticate(
        string resourceName, IClaimsPrincipal incomingPrincipal)
    {
        var authenticatedIdentities = incomingPrincipal.Identities.Where(x => x.IsAuthenticated);
        if (authenticatedIdentities.Any() &&
                authenticatedIdentities.Where(x => x.IsAuthenticated)
                                       .SelectMany(x => x.Claims)
                                       .Where(x => x.ClaimType == ClaimTypes.CustomClaim)
                                       .All(x => x.Value != expectedClaim))
        {
                throw new HttpException(
                    (int)HttpStatusCode.Unauthorized,
                    "User does not have access to the system");
        }
        return base.Authenticate(resourceName, incomingPrincipal);
    }
}

上述工作并阻止访问系统,但ti不是非常友好,因为用户只是得到500错误。此外,由于用户基本上已登录但无法访问,因此无法注销。我暴露了一个未经授权的错误页面,该页面提供了对匿名用户的访问权限,但我还没有办法重定向用户。

2 个答案:

答案 0 :(得分:4)

我想知道你为什么在身份验证管理器中实现了逻辑。

您是否考虑过切换到ClaimsAuthorizationManager?为此,您需要将ClaimsAuthorizationModule添加到管道中:

 <system.webServer>
    <modules>
        ...
        <add name="SessionAuthenticationModule" ....
        <add name="ClaimsAuthorizationModule" type="Microsoft.IndentityModel.Web.ClaimsAuthorizationModule, Microsoft.IndentityModel.Web" />
    </modules>
 </system.webServer>

然后您创建授权角色的结构:

 <claimsAuthorizationManager type="yourtype, yourassembly">
      <requiredClaim claimType="foobar" />
 </claimsAuthorizationManager>

并且在经理中:

public class FederatedClaimsAuthorizationManager : ClaimsAuthorizationManager
{
    private List<string> _requiredClaims = new List<string>();

    public FederatedClaimsAuthorizationManager( object config )
    {
        XmlNodeList nodes = config as XmlNodeList;
        foreach ( XmlNode node in nodes )
        {
            XmlTextReader xtr = new XmlTextReader( new StringReader( node.OuterXml ) );
            xtr.MoveToContent();

            _requiredClaims.Add( xtr.GetAttribute( "claimType" ) );
        }
    }

    public override bool CheckAccess( AuthorizationContext context )
    {
        IClaimsIdentity identity = context.Principal.Identities[0];

        return !identity.IsAuthenticated ||
                identity.Claims.Any( c => _requiredClaims.Any( rq => c.ClaimType == rq ) );
    }
}

这与您的方法之间的区别在于,只需重新调用身份验证过程即可处理失败的授权 - 没有例外,没有500。

例如 - 如果您在登录页面上使用<wif:FederatedPassiveSignIn />,则失败的授权只会将用户重定向到登录页面,您可以在其中检查用户是否经过身份验证并显示“您被重定向到登录页面,这可能意味着您试图访问您无权访问的资源“。

答案 1 :(得分:1)

除了转换声明的典型情况之外,文档不清楚如何实现Authenticate。如果您需要根据声明拒绝用户,则可以自行决定。

我试过返回null并抛出各种异常,包括401(类似于你的例子)。我发现最有效的解决方案是创建并返回一个匿名主体。

using System.Security.Principal;

...

public class CustomAuthenticationManager : ClaimsAuthenticationManager
{
    public override IClaimsPrincipal Authenticate(
        string resourceName, 
        IClaimsPrincipal incomingPrincipal)
    {
        ...

        ClaimsIdentity identity = new GenericIdentity(String.Empty);
        return new GenericPrincipal(identity, new string[0] { });
    }
}

您必须传递一个空字符串作为标识名称,以便在通用声明标识上使用IsAuthenticated返回false。