# License签名验证失败问题分析与解决方案 ## 问题描述 在项目中出现License签名验证失败的问题,根本原因是`YudaoJacksonAutoConfiguration`中配置的全局`LocalDateTime`序列化器与`LicenseGenerator`中的序列化配置不一致,导致时间字段在签名数据生成时格式不同。 ## 问题根本原因 ### 1. YudaoJacksonAutoConfiguration配置 在`YudaoJacksonAutoConfiguration`中配置了全局的`LocalDateTime`序列化器: ```java // 新增 LocalDateTime 序列化、反序列化规则,使用 Long 时间戳 .addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE) .addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE); ``` `TimestampLocalDateTimeSerializer`将`LocalDateTime`序列化为时间戳: ```java @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { // 将 LocalDateTime 对象,转换为 Long 时间戳 gen.writeNumber(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); } ``` ### 2. LicenseGenerator配置 `LicenseGenerator`使用标准的Jackson配置: ```java public LicenseGenerator() { this.objectMapper = new ObjectMapper(); this.objectMapper.registerModule(new JavaTimeModule()); this.objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 禁用时间戳格式 this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT); } ``` ### 3. 签名数据生成差异 - **LicenseGenerator生成签名时**:使用标准格式(如:`2024-01-15T10:30:00`) - **项目验证签名时**:由于全局配置,`LocalDateTime`被序列化为时间戳(如:`1705294200000`) 这导致签名数据字符串不同,进而导致签名验证失败。 ## 解决方案 ### 方案1:修改LicenseGenerator使用项目的序列化配置(推荐) 修改`LicenseGenerator`的构造函数,使其与项目保持一致: ```java public LicenseGenerator() { this.objectMapper = new ObjectMapper(); this.objectMapper.registerModule(new JavaTimeModule()); // 使用与项目一致的LocalDateTime序列化配置 SimpleModule simpleModule = new SimpleModule(); simpleModule.addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE); simpleModule.addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE); this.objectMapper.registerModule(simpleModule); this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT); } ``` ### 方案2:在CryptoUtil中使用独立的ObjectMapper 为License验证创建独立的`ObjectMapper`,不受全局配置影响: ```java private static ObjectMapper createLicenseObjectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return mapper; } ``` ### 方案3:修改签名数据生成逻辑 在`createSignatureData`方法中,对时间字段进行特殊处理,确保格式一致: ```java private void appendField(StringBuilder sb, String fieldName, Object value) { sb.append(fieldName).append("="); if (value != null) { if (value instanceof LocalDateTime) { // 统一使用时间戳格式 LocalDateTime dateTime = (LocalDateTime) value; long timestamp = dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); sb.append(timestamp); } else { sb.append(value.toString()); } } sb.append(";"); } ``` ## 推荐实施步骤 1. **立即解决**:采用方案1,修改`LicenseGenerator`使其与项目配置保持一致 2. **重新生成License文件**:使用修改后的`LicenseGenerator`重新生成所有License文件 3. **测试验证**:确保新生成的License文件能够正常验证 4. **文档更新**:更新相关文档,说明时间序列化的统一标准 ## 注意事项 1. 修改后需要重新生成所有现有的License文件 2. 确保开发、测试、生产环境使用相同的序列化配置 3. 建议在License生成和验证过程中添加详细的日志,便于问题排查 4. 考虑添加单元测试,验证签名生成和验证的一致性 ## 预防措施 1. 建立License生成和验证的集成测试 2. 在CI/CD流程中加入License兼容性检查 3. 统一项目中所有时间序列化的配置标准 4. 定期检查Jackson配置的变更对License系统的影响