最近把一个程序程序部署到Docker启动时一直报 no such file 错误。经排查,发现是程序启动加载本地证书文件时找不到该文件。加载代码如下:

    public static X509Certificate2 GetCertificate(IConfiguration configuration)
    {
		var certFullPath = Path.Combine(Directory.GetCurrentDirectory(), configuration["IdentityServer:Certificates:CerPath"]);
        return new X509Certificate2(certFullPath, configuration["IdentityServer:Certificates:Password"]);
    }

从配置文件读取相对路径,然后和当前目录combile获取绝对路径。本地运行是没有问题的。
首先猜想是不是 Directory.GetCurrentDirectory() 获取路径的方式在发布后发生了变化,于是改成下列方式获取,

        var assembly = typeof(IdentityServerConfig).GetTypeInfo().Assembly;
        string currentDirectory = Path.GetDirectoryName(assembly.Location);

        var certFullPath = Path.Combine(currentDirectory, configuration["IdentityServer:Certificates:CerPath"]);

结果仍然不成功。于是使用命令 docker cp {container_id}:{src_path} {dest_path} 将容器中的文件复制到本地,并对比了路径也没发现任何问题,更奇怪的时,本地运行该路径下的程序也能正常启动。
消耗N级脑细胞后,怀疑是不是加载file的方式在docker中不一样。因为docker中的file并没有类似磁盘 C:// 这样的结构。在参考了一些资料后,采用内嵌资源的方式最终完成了证书的读取。

    public static X509Certificate2 GetCertificate(IConfiguration configuration)
    {
        var assembly = typeof(IdentityServerConfig).GetTypeInfo().Assembly;
        //程序集命名空间.资源文件所在文件夹名.资源文件名
        using (var stream = assembly.GetManifestResourceStream("gt.IdentityServer.Security.certificate.idsrv4.pfx"))
        {
            return new X509Certificate2(ReadStream(stream), configuration["IdentityServer:Certificates:Password"]);
        }
    }

    private static byte[] ReadStream(Stream input)
    {
        byte[] buffer = new byte[16 * 1024];
        using (MemoryStream ms = new MemoryStream())
        {
            int read;
            while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                ms.Write(buffer, 0, read);
            }
            return ms.ToArray();
        }
    }

这里2个注意点:

  • 证书文件需要设置为 内嵌的资源
  • GetManifestResourceStream的参数格式为 程序集命名空间.资源文件所在文件夹名.资源文件名
posted on 2020-09-11 14:03  gt1987  阅读(826)  评论(0编辑  收藏  举报