You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Attribute构造函数需常量表达式:JSON加密动态密钥适配咨询

问题背景

我用Json.NET实现JSON属性加密功能,原本用Const常量作为加密密钥,现在想改用应用初始化时用户输入的唯一token作为密钥。但把标记加密属性的代码从:

<JsonProperty("endpoint")> <JsonEncrypt(EndpointKey)>

改成:

<JsonProperty("endpoint")> <JsonEncrypt(Settings.AppTokens("myapp"))>

(AppTokens是Settings类的Dictionary(Of String, String)属性,从基础JSON配置文件读取)时,IDE提示**"Constant expression is required"**错误。

我的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

我曾尝试用类似<JsonEncrypt(GetType(Settings), NameOf(Settings.AppTokens("myapp")))>的写法,但未适配成功。

问题

  1. 该错误产生的原因是什么?是否与JsonEncryptAttribute继承自System.Attribute有关?
  2. 有无可行的替代方案,能够兼容从JSON配置读取的动态密钥?

解答

问题1:错误原因

是的,这个错误和JsonEncryptAttribute继承自System.Attribute直接相关。.NET框架要求属性的构造函数参数必须是编译时常量——因为属性属于编译期元数据,会被嵌入程序集,无法在运行时动态计算参数值。

你之前用的EndpointKey是Const常量,属于编译时常量,符合要求;而Settings.AppTokens("myapp")是运行时才能获取的动态值(从配置文件读取、字典取值都是运行时操作),不符合属性构造参数的要求,因此触发错误。

问题2:替代方案

要支持动态密钥,需要调整加密逻辑,不再把密钥硬编码到属性元数据里,而是在序列化/反序列化过程中动态传入密钥。以下是两种可行方案:

方案1:自定义ContractResolver,结合属性标记动态获取密钥

  1. 修改JsonEncryptAttribute,去掉构造函数的密钥参数,只保留标记作用(可添加密钥标识):
<AttributeUsage(AttributeTargets.[Property] Or AttributeTargets.Field, AllowMultiple:=False)>
Public NotInheritable Class JsonEncryptAttribute
    Inherits Attribute
    ' 指定对应配置中的密钥名称
    Public Property KeyName As String
End Class
  1. 标记属性时指定密钥名称:
<JsonProperty("endpoint")> <JsonEncrypt(KeyName:="myapp")>
  1. 自定义ContractResolver,在创建属性序列化器时动态获取密钥:
Public Class EncryptedContractResolver
    Inherits DefaultContractResolver

    Protected Overrides Function CreateProperty(member As MemberInfo, memberSerialization As MemberSerialization) As JsonProperty
        Dim propertyObj = MyBase.CreateProperty(member, memberSerialization)
        Dim encryptAttr = member.GetCustomAttribute(Of JsonEncryptAttribute)()

        If encryptAttr IsNot Nothing Then
            ' 从Settings动态获取对应密钥
            Dim key = Settings.AppTokens(encryptAttr.KeyName)
            Dim encryptionKey = SHA256.HashData(Encoding.UTF8.GetBytes(key))
            
            ' 替换属性的序列化/反序列化逻辑
            propertyObj.ValueProvider = New EncryptedValueProvider(propertyObj.ValueProvider, encryptionKey)
        End If

        Return propertyObj
    End Function
End Class
  1. 实现EncryptedValueProvider处理加密解密逻辑:
Public Class EncryptedValueProvider
    Implements IValueProvider
    Private ReadOnly _innerProvider As IValueProvider
    Private ReadOnly _encryptionKey As Byte()

    Public Sub New(innerProvider As IValueProvider, encryptionKey As Byte())
        _innerProvider = innerProvider
        _encryptionKey = encryptionKey
    End Sub

    Public Sub SetValue(target As Object, value As Object) Implements IValueProvider.SetValue
        Dim encryptedStr = TryCast(value, String)
        If encryptedStr IsNot Nothing Then
            Dim decryptedValue = Decrypt(encryptedStr, _encryptionKey)
            _innerProvider.SetValue(target, decryptedValue)
        Else
            _innerProvider.SetValue(target, value)
        End If
    End Sub

    Public Function GetValue(target As Object) As Object Implements IValueProvider.GetValue
        Dim originalValue = _innerProvider.GetValue(target)
        If originalValue IsNot Nothing Then
            Return Encrypt(originalValue.ToString(), _encryptionKey)
        End If
        Return originalValue
    End Function

    ' 补充你的加密解密实现(复用原示例逻辑即可)
    Private Function Encrypt(input As String, key As Byte()) As String
        ' ...
    End Function

    Private Function Decrypt(input As String, key As Byte()) As String
        ' ...
    End Function
End Class
  1. 序列化/反序列化时使用该ContractResolver:
Dim jsonSettings = New JsonSerializerSettings() With {
    .ContractResolver = New EncryptedContractResolver()
}

' 序列化
Dim jsonStr = JsonConvert.SerializeObject(yourObject, jsonSettings)

' 反序列化
Dim targetObj = JsonConvert.DeserializeObject(Of YourClass)(jsonStr, jsonSettings)

方案2:使用JsonConverter通过上下文传递密钥

  1. 保留JsonEncryptAttribute作为标记,自定义JsonConverter:
Public Class EncryptedConverter
    Inherits JsonConverter
    Private ReadOnly _keyGetter As Func(Of String, Byte())

    Public Sub New(keyGetter As Func(Of String, Byte()))
        _keyGetter = keyGetter
    End Sub

    Public Overrides Function CanConvert(objectType As Type) As Boolean
        Return True
    End Function

    Public Overrides Sub WriteJson(writer As JsonWriter, value As Object, serializer As JsonSerializer)
        Dim prop = serializer.Context.Context As JsonProperty
        Dim encryptAttr = prop.AttributeProvider.GetAttributes(GetType(JsonEncryptAttribute), False).FirstOrDefault() As JsonEncryptAttribute
        
        If encryptAttr IsNot Nothing Then
            Dim key = _keyGetter(encryptAttr.KeyName)
            writer.WriteValue(Encrypt(value.ToString(), key))
        Else
            writer.WriteValue(value)
        End If
    End Sub

    Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
        Dim prop = serializer.Context.Context As JsonProperty
        Dim encryptAttr = prop.AttributeProvider.GetAttributes(GetType(JsonEncryptAttribute), False).FirstOrDefault() As JsonEncryptAttribute
        
        If encryptAttr IsNot Nothing Then
            Dim encryptedStr = reader.Value.ToString()
            Dim key = _keyGetter(encryptAttr.KeyName)
            Return Decrypt(encryptedStr, key)
        Else
            Return reader.Value
        End If
    End Function

    ' 补充加密解密实现
    Private Function Encrypt(input As String, key As Byte()) As String
        ' ...
    End Function

    Private Function Decrypt(input As String, key As Byte()) As String
        ' ...
    End Function
End Class
  1. 序列化时传入密钥获取逻辑:
Dim converter = New EncryptedConverter(Function(keyName) 
    Return SHA256.HashData(Encoding.UTF8.GetBytes(Settings.AppTokens(keyName)))
End Function)

Dim jsonSettings = New JsonSerializerSettings() With {
    .Converters = {converter}
}

' 后续序列化/反序列化操作使用jsonSettings即可

这两种方案都能避开属性构造参数必须为常量的限制,实现动态从配置读取密钥进行加密解密。


内容的提问来源于stack exchange,提问作者G_Hosa_Phat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:10:03