【问题标题】:How to define a System.Security.Claims.ClaimsPrincipal as a request argument in gRPC?如何将 System.Security.Claims.ClaimsPrincipal 定义为 gRPC 中的请求参数?
【发布时间】:2021-11-04 14:38:34
【问题描述】:

我想在 .Net Code 下使用 gRPC 构建授权服务。为此,我需要将 System.Security.Claims.ClaimsPrincipal 对象作为请求参数从调用者传递给服务器,以便服务器可以使用它来授权调用者。但我不知道怎么做——我怎样才能为一个标准库的类定义一个 .proto 。我该怎么办?

【问题讨论】:

    标签: asp.net-core authorization protocol-buffers grpc


    【解决方案1】:

    我也在做同样的事情,使用 protobuf-net grpc 库。 由于许多 Identity/Security 类(如果您正在使用它们)来自 Microsoft,因此您需要公开它们的成员以进行序列化;你可以使用:

    RuntimeTypeModel.Default.Add(typeof(SignInResult), false).Add(
                nameof(SignInResult.Succeeded),
                nameof(SignInResult.IsLockedOut),
                nameof(SignInResult.IsNotAllowed),
                nameof(SignInResult.RequiresTwoFactor)
            );
    

    并列出需要通过 gRpc 公开的成员。 至于 ClaimsPrincipal,具体来说,这就是我目前正在尝试实现的。对于声明,我使用的是代理类:

    RuntimeTypeModel.Default.Add(typeof(Claim), true).SetSurrogate(typeof(ClaimSurrogate));
    
    public class ClaimSurrogate
    {
        [DataMember, ProtoMember(1)]
        public string Type { get; set; }
        [DataMember, ProtoMember(2)]
        public ClaimsIdentity Subject { get; set; }
        [DataMember, ProtoMember(3)]
        public IDictionary<string, string> Properties { get; set; }
        [DataMember, ProtoMember(4)]
        public string OriginalIssuer { get; set; }
        [DataMember, ProtoMember(5)]
        public string Issuer { get; set; }
        [DataMember, ProtoMember(6)]
        public string ValueType { get; set; }
        [DataMember, ProtoMember(7)]
        public string Value { get; set; }
    
        public static implicit operator ClaimSurrogate(Claim claim)
        {
            if (claim == null)
                return null;
            return new ClaimSurrogate()
            {
                Type = claim.Type,
                Subject = claim.Subject,
                Properties = claim.Properties,
                OriginalIssuer = claim.OriginalIssuer,
                Issuer = claim.Issuer,
                ValueType = claim.ValueType,
                Value = claim.Value
            };
        }
    
        public static implicit operator Claim(ClaimSurrogate surrogate)
        {
            if (surrogate == null)
                return null;
            return new Claim(surrogate.Type, surrogate.Value, surrogate.ValueType, surrogate.Issuer, surrogate.OriginalIssuer, surrogate.Subject);
        }
    
    }
    

    而且我假设 ClaimsPrincipal 可以以同样的方式完成,但是,我遇到了麻烦。这就是我遇到你的问题的方式...... 实际上,通过尝试提供答案...从字面上看,我刚刚意识到我忽略了什么,我还需要为 ClaimsIdentity 设置一个代理项

    到目前为止,我需要“第三方”类的代理;只有属性。 ClaimsPrincipal 具有这些类型的属性,ClaimsIdentity 也是如此(Claim 也是如此)。如果 ClaimsIdentitySurrogate 成功了,我会更新/评论

    更新:

    是的,可以做到。 ClaimsIdentity 和 IIdentity 将需要代理,如上面的示例。这些类用作 ClaimsPrincipal 中的成员/属性。 ClaimsIdentity:您可以将 SetSurrogate 和 Add(nameof(...)) 混合在一起,因为它有 get only 和 get/sets(get/sets 位于 Add 部分)。不要在 ClaimsIdentity 代理中包含 Actor,因为它会在您的服务启动中创建一个永无止境的循环。如果确实包含它,请确保它不是 DataMember/Protomember。并且(私有)将其设置在代理运算符中。与声明相同。

    基本上,任何具有引用父类的成员的代理,或具有引用此父类型的代理的其他类型的代理,都会创建循环引用并在启动时出错。

    IIdentity:这是一个简单的,只是 RuntimeTypeModel.Default.Add(typeof(IIdentity), false)。

    最后(当我认为我拥有它时,我发布了这个更新,但是,在所有 UT 测试和更改等中,我发布得有点早;在对 ClaimPrincipal 代理类进行了重大更改之后)....

    您需要一个将在您的 ClaimPrincipal 代理中使用的 IIdentity 虚拟类,而不是 IIdentity Identity {get;set;}。这个虚拟类应该继承自 IIdentity,例如

    [DataContract]
    public class IIdentityFraud : System.Security.Principal.IIdentity
    

    在代理的隐式运算符中:

    IIdentityFraud identityfraud = null;
    if (claimPrincipal.Identity != null)
    {
         identityfraud = new IIdentityFraud(claimPrincipal.Identity.AuthenticationType, claimPrincipal.Identity.Name, claimPrincipal.Identity.IsAuthenticated);
    }
    

    更新(2021 年 11 月 5 日):

    [DataContract]
        public class ClaimsPrincipalSurrogate
        {
            [DataMember, ProtoMember(1)]
            public IIdentityFraud Identity { get; set; }
            [DataMember, ProtoMember(2)]
            public IEnumerable<ClaimsIdentity> Identities { get; set; }
            [DataMember, ProtoMember(3)]
            public IEnumerable<Claim> Claims { get; set; }
    
            public static implicit operator ClaimsPrincipalSurrogate(ClaimsPrincipal claimPrincipal)
            {
                if (claimPrincipal == null)
                {
                    return null;
                }
                else
                {
                    IIdentityFraud identityfraud = null;
                    if (claimPrincipal.Identity != null)
                    {
                        identityfraud = new IIdentityFraud(claimPrincipal.Identity.AuthenticationType, claimPrincipal.Identity.Name, claimPrincipal.Identity.IsAuthenticated);
                    }
    
                    return new ClaimsPrincipalSurrogate()
                    {
                        Identity = identityfraud, // (System.Security.Principal.IIdentity)identityfraud,
                        Identities = claimPrincipal.Identities,
                        Claims = claimPrincipal.Claims
                    };
                }
            }
    
            public static implicit operator ClaimsPrincipal(ClaimsPrincipalSurrogate surrogate)
            {
                if (surrogate == null)
                    return null;
    
                if (surrogate.Identities != null && surrogate.Identities.Any() == true)
                {
                    return new ClaimsPrincipal(surrogate.Identities);
                }
                else if (surrogate.Identity != null)
                {
                    return new ClaimsPrincipal(surrogate.Identity);
                }
                return new ClaimsPrincipal();
            }
    
        }
    
        [DataContract]
        public class ClaimsIdentitySurrogate
        {
            [DataMember, ProtoMember(1)]
            public string AuthenticationType { get; set; }
            [DataMember, ProtoMember(2)]
            public string Name { get; set; }
            //[DataMember, ProtoMember(3)]
            //public string Label { get; set; }
            [DataMember, ProtoMember(4)]
            public bool IsAuthenticated { get; set; }
            [DataMember, ProtoMember(5)]
            public IEnumerable<Claim> Claims { get; private set; }
            //[DataMember, ProtoMember(6)]
            //public object BootstrapContext { get; set; }
            //[DataMember, ProtoMember(7)]
            public ClaimsIdentity Actor { get; private set; }
            [DataMember, ProtoMember(8)]
            public string RoleClaimType { get; set; }
            [DataMember, ProtoMember(9)]
            public string NameClaimType { get; set; }
    
            public static implicit operator ClaimsIdentitySurrogate(ClaimsIdentity claimIdentity)
            {
                if (claimIdentity == null)
                    return null;
                return new ClaimsIdentitySurrogate()
                {
                    AuthenticationType = claimIdentity.AuthenticationType,
                    Name = claimIdentity.Name,
                    //Label = claimIdentity.Label,
                    IsAuthenticated = claimIdentity.IsAuthenticated,
                    Claims = claimIdentity.Claims,
                    //BootstrapContext = claimIdentity.AuthenticationType,
                    Actor = claimIdentity.Actor,
                    RoleClaimType = claimIdentity.RoleClaimType,
                    NameClaimType = claimIdentity.NameClaimType
                };
            }
    
            public static implicit operator ClaimsIdentity(ClaimsIdentitySurrogate surrogate)
            {
                if (surrogate == null)
                {
                    return null;
                }
    
                if (surrogate.Claims?.Any() == true)
                {
                    return new ClaimsIdentity(surrogate.Claims, surrogate.AuthenticationType);
                }
                else
                {
                    return new ClaimsIdentity(surrogate.AuthenticationType, surrogate.NameClaimType, surrogate.RoleClaimType);
                }
            }
        }
    
    
    [DataContract]
    public class IIdentityFraud : System.Security.Principal.IIdentity
    {
        [DataMember, ProtoMember(1)]
        public string AuthenticationType { get; private set; }
        [DataMember, ProtoMember(2)]
        public string Name { get; private set; }
        [DataMember, ProtoMember(3)]
        public bool IsAuthenticated { get; private set; }
    
        public IIdentityFraud() { }
        public IIdentityFraud(string authenticationType, string name, bool isAuthenticated)
        {
            this.AuthenticationType = authenticationType;
            this.Name = name;
            this.IsAuthenticated = isAuthenticated;
        }
    }
    
    
    [DataContract] //don't know if this is really needed. Too involved in testing out the rest of it and have yet to come back to this.
    public class IIdentitySurrogate : System.Security.Principal.IIdentity
    {
        [DataMember, ProtoMember(1)]
        public string AuthenticationType { get; set; }
        [DataMember, ProtoMember(2)]
        public string Name { get; set; }
        [DataMember, ProtoMember(3)]
        public bool IsAuthenticated { get; set; }
    
        public static implicit operator IIdentitySurrogate(IIdentityFraud iidentity)
        {
            if (iidentity == null)
                return null;
            return new IIdentitySurrogate()
            {
                AuthenticationType = iidentity.AuthenticationType,
                Name = iidentity.Name,
                IsAuthenticated = iidentity.IsAuthenticated
            };
        }
    
        public static implicit operator IIdentityFraud(IIdentitySurrogate surrogate)
        {
            if (surrogate == null)
                return null;
    
            return new IIdentityFraud(surrogate.AuthenticationType, surrogate.Name, surrogate.IsAuthenticated);
        }
    
    }
    

    更多在初创公司上执行的操作:

    #region ClaimsIdentity
                RuntimeTypeModel.Default.Add(typeof(ClaimsIdentity), true).Add(
                    nameof(ClaimsIdentity.Label),
                    nameof(ClaimsIdentity.BootstrapContext),
                    nameof(ClaimsIdentity.Actor)
                ).SetSurrogate(typeof(ClaimsIdentitySurrogate));
                #endregion ClaimsIdentity
    
                #region ClaimsPrincipal
                RuntimeTypeModel.Default.Add(typeof(ClaimsPrincipal), true).SetSurrogate(typeof(ClaimsPrincipalSurrogate));
                #endregion ClaimsPrincipal
    
                #region IIdentity
                RuntimeTypeModel.Default.Add(typeof(IIdentity), true);
                #endregion IIdentity
    

    【讨论】:

    • 感谢 fafafooey 的输入。请让我随时了解您的实验。一旦我测试了您的解决方案,我将接受您的回答。
    • @Alexu 可以,你需要为 ClaimsPrincipal 和 ClaimsIdentity 设置代理(如果需要)
    • 是的,我之前尝试过,但无法完全通过。部分原因是我不知道如何处理这些虚拟属性和函数,例如“public virtual string?Name { get; }”和“public static Func ClaimsPrincipalSelector”。
    • 也许不需要这些?您介意发布您的 ClaimsPrincipalSurrogate 和 ClaimsIdentitySurrogate 类吗?
    • 我仍然无法使代码正常工作。我使用了你所有的类并添加了 RuntimeTypeModel.Default.Add(typeof(typeOne), false).SetSurrogate(typeof(typeOneSurrogate));对于客户端和服务器端静态构造函数上的所有 4 种类型。当我尝试它时,我得到一个异常,上面写着“System.InvalidOperationException:没有为类型定义序列化程序:System.Security.Claims.ClaimsIdentity”。我觉得有趣的是它只抱怨 ClaimsIdentity,而不是 ClaimsPrincipal。但为什么我会得到这个?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-10
    • 1970-01-01
    • 2017-07-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多