如何解决本地DLL 的PublicKey Token与项目需要的DLL的PublicKey Token不一致的问题

解决Log4Net版本冲突
本文介绍了解决Log4Net版本及公钥令牌不匹配导致的异常问题的过程,包括如何定位问题、查找正确版本的dll文件及使用配置文件进行版本映射。

这个过程是这样的。有一个遗留的项目需要做一些修改,那么我就从TFS上把这个项目下载下来,用VisualStudio打开。编译时没有发现错误,直接运行。可是运行时就抛出了异常。

Could not load file or assembly 'log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821' or one of its dependencies. 
The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)":"log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821"}

我的第一反应是这个Log4Net.dll找不到,觉得那么在reference列表中这个dll应该是黄色才对。右键查看项目引用的dll,发现Log4Net.dll正常引用,并没有黄色叹号,说明找到了这个dll.(后来想想也对,如果有黄色叹号标记的dll,那么说明找不到这个dll,那么编译的时候就应该报出异常,说找不到这个dll,所以由此总结,带黄色叹号的都是项目要用到的,但是没有在本地找到的,编译不会通过。如果没有黄色叹号抛出上边的异常,那么就是这个dll找到了,但是可能版本不一致。)

双击reference列表中的Log4Net.dll,可以看到其详细的信息,可以看出,这个dll指向的路径是GAC中,那么我就去GAC中找,确实在我本机找到了。


这个程序集存在于GAC中。那么我们查看一下这个Log4Net的dll信息。





果然,这里的publicKey Token与所需要的PublicKeyToken=1b44e1d426115821不一致。那么怎么解决这个问题?以前记得解决DLLversin不一样问题的时候,可以使用web.config或者app.config中的runtime节点进行redirect映射,那么publicTokenKey 可以吗?我找了找,没有找到相关配置。怎么办? 那么就去找指定publicKey token的dll吧。去哪里找?去Log4Net的官网找。确实,官网说明了它的dll变过publicKey token.



不过一般来讲,一个公司发布产品类型的dll,本不应该变换publickey Token签名的,否则会给用户带来像本文提到的这种问题,不同的人机器人可能安装了不同publickeytoken的签名版本,造成混乱。但是不知道为什么Log4Net却中途变了这个签名,有newkey与oldkey版本。

我下载了new key的版本,觉得这个应该是项目需要的,但是怎么确定一下这个下载来的dll的publickkeytoken呢?这里就可以用VS自带的工具。打开VS命令行,输入sn命令就可以。



由此可以看到这个publickey token不是。所以继续下载了oldkey的dll,使用同样的方法,最终证实确实是需要oldkey的版本。所以,我下载下来这个Log4Net.dll,然后在项目中建立一个Lib目录,把dll放进去,让项目对其添加引用。publickeyToken对了,此时我们需要看一下,那么版本对吗?右键查看这个新引用的dll的property,发现版本还真不对。项目需要Version=1.2.10.0,而这个新下载的是1.2.13.0,那么怎么办?这时就可以用版本映射来解决了。打开配置问题件,在节点中增加这些信息,意思就是需要1.2.10.0的时候,如果遇到了1.2.13.0,那么就可以认为找到了对应的dll。

 

至此,编译运行,发现问题解决,没有再报任何的异常。


一些题外话:

以上的问题,都是由于项目开发时候的不规范造成的。像Log4Net这样的非微软集成到GAC中的dll,就应该在项目中建立一个Lib目录存储下来,以后当有人下载代码的时候,能够连带dll一起下载到本地,不用再大费周章的去找对应版本了。现在有时候是用Nuget就很好,那么额外的dll全会放到package中,并且include到项目中。

 

还有关于dll version的映射问题,这个不是什么时候都OK的。

比如说,现在我用到了一个第三方的dll,叫做SuperLog.dll,这个dll是一个公司的产品,这个公司是引用EntrepriseLogging.dll,引用的是5.0的版本,并且在这个基础之上增加功能。我们在一个项目中用到了SuperLog.dll,我们加入这个SuperLog.dll,编译时告诉我们需要引入EntrepriseLogging.dll,但是我们手头只有EntrepriseLogging.dll6.0的版本。或许我们可以引入6.0的版本,但是重新映射到5.0版本。但是,我们这样做了,编译的时候还是遇到了问题。异常好像是说,EntrepriseLogging没有某个方法之类的。所以,这个意思就是说,以前5.0的时候,可能有一个方法A(),正好被SuperLog调用了。但是在6.0版本的时候,EntrepriseLogging决定不要这个方法了。所以在我们项目中,虽然没有抛出版本不一样的异常,但是当把6.0的版本当5.0的用,对这个6.0版本掉用A方法,但是没有,就抛出了异常。


一般来讲,第三方DLL发布新的版本,基本上不会更新对外暴露的方法,只是会更新方法中的内容。所以,不管哪个版本我们用到的都是同一个方法。但是如果真的对外的方法都变了,增加了活减少了什么的,那么就会遇到上边的问题。所以,不是所有的版本不同问题都能通过配置映射来解决。

 

为什么要有PublickKey Token?

其中一个很重要的原因就是安全角度考虑。这样说吧,某公司开发了一个桌面程序A.exe,然后安装包供下载。你下载完成,并且安装。那么,在你的ProgramFile里边,就有A.exe与一些供其调用的dll,比如说B.dll,当你运行A的时候,会调用B.dll,完成相应操作。现在,如果有某个非法程序偷偷的跑到A的目录中,找到B.dll,然后把自己写的同名的dll替换原来的。这时运行A的时候,调用B,就可能会有一些恶意非法操作。PublicKey的作用之一就是用来验证B是否是完全一致的B。验证机制比较复杂,大家可以进一步探索。


<think>好的,用户之前问过关于JwtDecoder设置时区的问题,现在又问了如何在Spring Security OAuth2 Resource Server中使用publicKey字符串。我需要看看这两个问题之间有没有关联,或者用户是否在构建自己的资源服务器时遇到了配置问题。 用户这次的需求很明确,就是想使用文件或URL来加载公钥,而是直接使用字符串形式的公钥。这可能是因为他们希望将公钥嵌入到配置文件中,或者从环境变量中读取,避免维护额外的文件。这在实际部署中可能更方便,尤其是云原生应用,配置通常通过环境变量或配置中心管理。 首先,我需要回忆Spring Security OAuth2 Resource Server的配置方式。通常,配置公钥是通过jwk-set-uri或者直接指定公钥的位置,比如文件。但用户希望用字符串,所以可能需要直接解析公钥字符串成PublicKey对象。 接下来,我需要确定如何将PEM格式的公钥字符串转换为Java的PublicKey对象。用户提供的公钥可能以-----BEGIN PUBLIC KEY-----开头,这是标准的PEM格式。Java需要的是RSAPublicKey实例,所以需要解析PEM字符串,提取Base64编码的内容,转换为X.509证书,然后获取公钥。 然后,需要考虑如何在Spring的配置中注入这个PublicKey。通常,JwtDecoder是通过NimbusJwtDecoder的withPublicKey方法创建的,所以需要将字符串转换为PublicKey后传入。 可能的步骤是:编写一个工具类,将PEM字符串转换为PublicKey;然后在配置类中创建Bean,比如PublicKey publicKey(),调用这个工具方法;最后创建JwtDecoder时使用这个PublicKey。 需要注意异常处理,比如无效的PEM格式,或者解析过程中的错误,应该抛出明确的异常信息,方便用户调试。 另外,用户可能有疑问,如何验证配置是否正确。可能需要建议用户使用生成的JwtDecoder来解析一个测试Token,或者检查日志是否有解码错误。 还需要考虑是否支持同的算法,比如RSA或ECDSA。用户提供的公钥类型可能会影响解析方式,但常见的是RSA,所以示例可能以RSA为例。 最后,确保代码示例正确,比如正确处理PEM头尾,移除换行符和空格,正确解码Base64内容,然后使用KeyFactory生成公钥对象。 可能还需要提到依赖,比如Bouncy Castle库,但Java标准库应该可以处理X.509证书的解析,所以可能需要额外依赖。 总结步骤:解析PEM字符串为PublicKey,配置JwtDecoder使用这个PublicKey,并验证配置是否正确。</think>--- ### 一、核心解决方案 **在 Spring Security OAuth2 Resource Server 中,可以通过以下步骤直接使用 PEM 格式的公钥字符串配置 JWT 验证:** --- ### 二、具体实现步骤 #### 1. 准备公钥字符串 确保公钥为标准 PEM 格式: ```java String publicKeyPem = """ -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo ...(省略实际内容)... ZKLk0KgWwIDAQAB -----END PUBLIC KEY-----"""; ``` --- #### 2. 创建公钥转换工具类 ```java import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; public class KeyUtils { public static PublicKey parsePublicKey(String pem) { try { // 去除 PEM 头尾标记和换行符 String publicKeyContent = pem .replace("-----BEGIN PUBLIC KEY-----", "") .replace("-----END PUBLIC KEY-----", "") .replaceAll("\\s", ""); // 移除所有空白字符 // Base64 解码 byte[] keyBytes = Base64.getDecoder().decode(publicKeyContent); // 创建 X.509 格式密钥规范 X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); // 使用 RSA 算法生成公钥 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePublic(spec); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { throw new RuntimeException("公钥解析失败", e); } } } ``` --- #### 3. 配置 JwtDecoder 在 Spring Security 配置类中注入 PublicKey: ```java import org.springframework.context.annotation.Bean; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; @Configuration public class SecurityConfig { @Bean public PublicKey publicKey() { String publicKeyPem = "-----BEGIN PUBLIC KEY-----..."; // 替换为实际公钥 return KeyUtils.parsePublicKey(publicKeyPem); } @Bean public JwtDecoder jwtDecoder(PublicKey publicKey) { return NimbusJwtDecoder.withPublicKey(publicKey).build(); } } ``` --- ### 三、关键验证点 | 验证项 | 操作指引 | |----------------------|------------------------------------------------------------------------| | **公钥格式验证** | 检查是否包含 `BEGIN/END PUBLIC KEY` 标记,内容为 Base64 编码的 DER 格式 | | **算法匹配验证** | 确认 JWT 签名算法(如 RS256)公钥类型(RSA)一致 | | **解码测试** | 使用有效/无效令牌测试 `JwtDecoder` 的解析能力 | --- ### 四、常见问题排查 #### 1. `InvalidKeySpecException` 错误 **原因**:公钥格式错误或算法匹配 **解决方案**: - 使用 OpenSSL 验证公钥有效性: ```bash openssl rsa -pubin -in public_key.pem -text ``` - 确保公钥生成命令正确: ```bash openssl genpkey -algorithm RSA -out private_key.pem openssl rsa -pubout -in private_key.pem -out public_key.pem ``` --- #### 2. 令牌验证失败 **调试方法**: ```java @Bean public JwtDecoder jwtDecoder(PublicKey publicKey) { NimbusJwtDecoder decoder = NimbusJwtDecoder.withPublicKey(publicKey).build(); decoder.setJwtValidator(new JwtValidator() { @Override public OAuth2TokenValidatorResult validate(Jwt jwt) { System.out.println("JWT 头信息:" + jwt.getHeaders()); System.out.println("JWT 声明:" + jwt.getClaims()); return OAuth2TokenValidatorResult.success(); } }); return decoder; } ``` --- ### 五、扩展方案对比 | 配置方式 | 优点 | 缺点 | |------------------|---------------------------|--------------------------| | **公钥字符串** | 配置灵活,无需文件 | 需处理 PEM 格式转换 | | **jwk-set-uri** | 支持动态密钥轮换 | 依赖网络可用性 | | **证书文件** | 符合传统密钥管理习惯 | 需要维护物理文件 | --- ### 六、完整代码示例 [GitHub Gist 参考示例](https://gist.github.com/example/oauth2-public-key-config) (包含完整的 PEM 处理工具类和异常处理)
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值