我正在尝试使用问题中的代码实现加密的 JSON 属性 使用 Json.NET,在序列化我的对象时如何加密任何类型的选定属性?。我最初在我的应用程序中设置了一些“密钥”作为常量 (
Const
),但我最近想到使用另一个更独特的值来应用此加密 - 用户将需要在期间输入的令牌应用程序的初始设置。但是,当我尝试更改为使用该令牌而不是原始密钥时,我遇到了 Constant expression is required
消息。
用于标记 JSON 属性以进行加密的原始工作代码如下所示:
<EditorBrowsable(EditorBrowsableState.Never)>
Private Class MyEndpoint
<JsonProperty("endpoint")> <JsonEncrypt(EndpointKey)>
Public Property Endpoint As Endpoint
End Class
<EditorBrowsable(EditorBrowsableState.Never)>
Private Class MyConnectionString
<JsonProperty("connection_string")> <JsonEncrypt(ConnectionStringKey)>
Public Property ConnectionString As ConnectionString
End Class
<EditorBrowsable(EditorBrowsableState.Never)>
Private Class MyUserCredential
<JsonProperty("credentials")> <JsonEncrypt(CredentialKey)>
Public Property Credential As Credential
End Class
我有这样的
JsonEncryptAttribute
课程:
<AttributeUsage(AttributeTargets.[Property] Or AttributeTargets.Field, AllowMultiple:=False)>
Public NotInheritable Class JsonEncryptAttribute
Inherits Attribute
Public Property EncryptionKey As Byte()
Public Sub New()
Me.EncryptionKey = GenerateKey(DefaultKey)
End Sub
Public Sub New(ByVal Password As String)
Me.EncryptionKey = GenerateKey(Password)
End Sub
Private Function GenerateKey(ByVal Password As String) As Byte()
Return SHA256.HashData(Encoding.UTF8.GetBytes(Password))
End Function
End Class
但是,当我将
MyEndpoint
类定义更改为看起来像 this:
<EditorBrowsable(EditorBrowsableState.Never)>
Private Class MyEndpoint
<JsonProperty("endpoint")> <JsonEncrypt(Settings.AppTokens("myapp"))>
Public Property Endpoint As Endpoint
End Class
...其中
AppTokens
是 Settings
类中的一个属性,它将被单独保存和读取,IDE 犹豫并给我 Constant expression is required
消息。基于 JsonEncryptAttribute
类的构造函数,我对 why 一开始就要求值是 Const
完全感到困惑。
但是,查看文档,我认为错误与
AppTokens
(定义为Dictionary(Of String, String)
)是最有可能的罪魁祸首这一事实有关:
语句没有正确初始化常量,或者数组声明使用变量来指定元素的数量。Const
错误编号:BC30059
纠正这个错误
如果声明是
语句,请检查以确保使用文字、先前声明的常量、枚举成员或文字、常量和枚举成员与运算符的组合来初始化常量。Const
如果声明指定了数组,请检查是否使用了变量来指定元素的数量。如果是这样,用常量表达式替换变量。
我看了另一个问题的自我接受的答案 - 枚举属性中的变量:需要常量表达式 - 并尝试实现类似的东西但是,除非我只是做错了(确定的可能性),它似乎不适用于类属性。
<JsonEncrypt(GetType(Settings), NameOf(Settings.AppTokens("myapp")))>
所以,现在我剩下以下问题:
任何人都可以向我解释为什么这里发生这个错误?跟
JsonEncryptAttribute
继承基数System.Attribute
有关系吗?
有没有办法通过使用不同的对象类型或其他东西来解决这个问题?
AppTokens
是从应用程序的“基本”JSON 配置文件中读取的,因此任何替代方案都必须与之兼容。
我相信我已经找到了一个可行的解决方案,我可以使用令牌作为加密密钥。它只需要对我所拥有的进行一点重构和重组。在阅读了How to set dynamic value in my Attribute之后,我将之前定义为单独的
EndpointKey
字段的ConnectionStringKey
、CredentialKey
和Const
值并将它们转换为枚举:
Public Enum ApplicationKeys
UserToken
<Description("Default Encryption Key")> DefaultKey
<Description("Encrypted Setting Key")> Setting
<Description("Encrypted ConnectionString Key")> ConnectionString
<Description("Encrypted Endpoint Key")> Endpoint
<Description("Encrypted Credential Key")> Credential
End Enum
然后,我为
JsonEncryptAttribute
设置了一个新的构造函数,它接受这个枚举作为参数:
Public Sub New(ByVal Key As ApplicationKeys)
If Key = ApplicationKeys.UserToken Then
Me.EncryptionKey = GenerateKey(Settings.AppTokens("myapp"))
Else
Me.EncryptionKey = GenerateKey(DirectCast([Enum].Parse(GetType(ApplicationKeys), Key), ApplicationKeys).GetDescription)
End If
End Sub
这使用
GetDescription
扩展方法† 为我可能想使用它们的任何地方提取“预定义”键值,而且如果选择了该枚举,我还可以绘制动态令牌。
显然,我需要在其中添加一些额外的错误处理,以应对标记为
Nothing
或空字符串的可能性,或者如果传递给构造函数的值未定义为 ApplicationKeys
的一部分枚举,但我相信这将提供我真正想要实现的解决方案。
† 只是为了帮助将来可能会看到这个的其他人,这里是
GetDescription
扩展方法:
''' <summary>
''' Gets the Description attribute of an Enum value
''' </summary>
''' <param name="CurrentEnum">The specific Enum value for which to get the Description attribute</param>
''' <returns>String value representing the Description attribute of the specified Enum</returns>
''' <remarks>Requires System.Reflection, System.ComponentModel and System.Runtime.CompilerServices namespaces to be imported.
''' The Description attribute of an Enum is set in the Enum declaration.'</remarks>
<DebuggerStepThrough>
<Extension()>
Public Function GetDescription(ByVal CurrentEnum As [Enum]) As String
Dim DescAttribute As DescriptionAttribute
Dim Field As FieldInfo = CurrentEnum.GetType.GetField(CurrentEnum.ToString)
DescAttribute = DirectCast(Attribute.GetCustomAttribute(Field, GetType(DescriptionAttribute)), DescriptionAttribute)
If DescAttribute IsNot Nothing Then
Return DescAttribute.Description
Else
Return CurrentEnum.ToString
End If
End Function