feat: 支持加密保护数据库连接字符串中的密码字段,便于配置中心使用。https://github.com/NewLifeX/NewLife.XCode/issues/14

This commit is contained in:
智能大石头 2023-05-11 07:34:03 +08:00
parent 2fe23f93d5
commit a6a54119ea
7 changed files with 87 additions and 57 deletions

View File

@ -57,6 +57,7 @@ namespace XCode.DataAccessLayer
String ConnName { get; set; }
/// <summary>链接字符串</summary>
/// <remarks>内部密码字段已解密</remarks>
String ConnectionString { get; set; }
/// <summary>数据库提供者。用于选择驱动</summary>

View File

@ -10,6 +10,7 @@ using NewLife.Configuration;
using NewLife.Log;
using NewLife.Model;
using NewLife.Reflection;
using NewLife.Security;
using NewLife.Serialization;
using NewLife.Threading;
@ -36,10 +37,18 @@ public partial class DAL
/// <summary>连接字符串</summary>
/// <remarks>
/// 内部密码字段可能处于加密状态。
/// 修改连接字符串将会清空<see cref="Db"/>
/// </remarks>
public String ConnStr { get; private set; }
/// <summary>数据保护者</summary>
/// <remarks>
/// 用于保护连接字符串中的密码字段在向IDatabase设置连接字符串前解密。
/// 默认保护密码可通过环境变量或者配置文件的ProtectedKey项进行设置。
/// </remarks>
public ProtectedKey ProtectedKey { get; set; } = ProtectedKey.Instance;
private IDatabase _Db;
/// <summary>数据库。所有数据库操作在此统一管理强烈建议不要直接使用该属性在不同版本中IDatabase可能有较大改变</summary>
public IDatabase Db
@ -522,10 +531,12 @@ public partial class DAL
/// <remarks>Base64=>UTF8字节=>明文</remarks>
/// <param name="connstr"></param>
/// <returns></returns>
private static String DecodeConnStr(String connstr)
private String DecodeConnStr(String connstr)
{
if (String.IsNullOrEmpty(connstr)) return connstr;
connstr = ProtectedKey.Unprotect(connstr);
// 如果包含任何非Base64编码字符直接返回
foreach (var c in connstr)
{

View File

@ -44,7 +44,7 @@
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="NewLife.Core" Version="10.3.2023.503" />
<PackageReference Include="NewLife.Core" Version="10.3.2023.510-beta2316" />
</ItemGroup>
<ItemGroup>
<Compile Remove="build\**" />

View File

@ -30,7 +30,7 @@
<!--命名格式。Default/Upper/Lower/Underline-->
<NameFormat>Default</NameFormat>
<!--生成器版本-->
<Version>11.8.2023.0503</Version>
<Version>11.8.2023.0511</Version>
<!--帮助文档-->
<Document>https://newlifex.com/xcode/model</Document>
</Option>

View File

@ -30,7 +30,7 @@
<!--命名格式。Default/Upper/Lower/Underline-->
<NameFormat>Underline</NameFormat>
<!--生成器版本-->
<Version>11.8.2023.0503</Version>
<Version>11.8.2023.0511</Version>
<!--帮助文档-->
<Document>https://newlifex.com/xcode/model</Document>
</Option>

View File

@ -1,74 +1,89 @@
using System;
using System.Collections.Concurrent;
using NewLife;
using XCode.DataAccessLayer;
using Xunit;
namespace XUnitTest.XCode.DataAccessLayer
namespace XUnitTest.XCode.DataAccessLayer;
public class DALTests
{
public class DALTests
[Fact]
public void LoadConfig()
{
[Fact]
public void LoadConfig()
{
var ds = new ConcurrentDictionary<String, DbInfo>(StringComparer.OrdinalIgnoreCase);
var ds = new ConcurrentDictionary<String, DbInfo>(StringComparer.OrdinalIgnoreCase);
DAL.LoadConfig(ds);
DAL.LoadConfig(ds);
Assert.True(ds.ContainsKey("MSSQL"));
Assert.True(ds.ContainsKey("MSSQL"));
var di = ds["MSSQL"];
Assert.Equal("MSSQL", di.Name);
Assert.Equal("Data Source=.;Initial Catalog=master;Integrated Security=SSPI", di.ConnectionString);
Assert.Equal("XCode.DataAccessLayer.SqlServer", di.Type.FullName);
Assert.Equal("System.Data.SqlClient", di.Provider);
}
var di = ds["MSSQL"];
Assert.Equal("MSSQL", di.Name);
Assert.Equal("Data Source=.;Initial Catalog=master;Integrated Security=SSPI", di.ConnectionString);
Assert.Equal("XCode.DataAccessLayer.SqlServer", di.Type.FullName);
Assert.Equal("System.Data.SqlClient", di.Provider);
}
[Fact]
public void LoadAppSettings()
{
var ds = new ConcurrentDictionary<String, DbInfo>(StringComparer.OrdinalIgnoreCase);
DAL.LoadAppSettings("appsettings.json", ds);
[Fact]
public void LoadAppSettings()
{
var ds = new ConcurrentDictionary<String, DbInfo>(StringComparer.OrdinalIgnoreCase);
DAL.LoadAppSettings("appsettings.json", ds);
Assert.True(ds.ContainsKey("sqlserver"));
Assert.True(ds.ContainsKey("sqlserver"));
var di = ds["sqlserver"];
Assert.Equal("sqlserver", di.Name);
Assert.Equal("Server=127.0.0.1;Database=Membership;Uid=root;Pwd=root;", di.ConnectionString);
Assert.Equal("XCode.DataAccessLayer.SqlServer", di.Type.FullName);
Assert.Equal("SqlServer", di.Provider);
}
var di = ds["sqlserver"];
Assert.Equal("sqlserver", di.Name);
Assert.Equal("Server=127.0.0.1;Database=Membership;Uid=root;Pwd=root;", di.ConnectionString);
Assert.Equal("XCode.DataAccessLayer.SqlServer", di.Type.FullName);
Assert.Equal("SqlServer", di.Provider);
}
[Fact]
public void LoadAppSettings2()
{
var ds = new ConcurrentDictionary<String, DbInfo>(StringComparer.OrdinalIgnoreCase);
DAL.LoadAppSettings("appsettings.json", ds);
[Fact]
public void LoadAppSettings2()
{
var ds = new ConcurrentDictionary<String, DbInfo>(StringComparer.OrdinalIgnoreCase);
DAL.LoadAppSettings("appsettings.json", ds);
Assert.True(ds.ContainsKey("sqlite"));
Assert.True(ds.ContainsKey("sqlite"));
var di = ds["sqlite"];
Assert.Equal("sqlite", di.Name);
Assert.Equal("Data Source=Data\\Membership.db;provider=sqlite", di.ConnectionString);
Assert.Equal("XCode.DataAccessLayer.SQLite", di.Type.FullName);
Assert.Equal("sqlite", di.Provider);
}
var di = ds["sqlite"];
Assert.Equal("sqlite", di.Name);
Assert.Equal("Data Source=Data\\Membership.db;provider=sqlite", di.ConnectionString);
Assert.Equal("XCode.DataAccessLayer.SQLite", di.Type.FullName);
Assert.Equal("sqlite", di.Provider);
}
[Fact]
public void LoadEnvironmentVariable()
{
var ds = new ConcurrentDictionary<String, DbInfo>(StringComparer.OrdinalIgnoreCase);
var envs = Environment.GetEnvironmentVariables();
envs.Add("XCode_pgsql", "Server=.;Database=master;Uid=root;Pwd=root;provider=PostgreSql");
[Fact]
public void LoadAppSettingsWithProtected()
{
var ds = new ConcurrentDictionary<String, DbInfo>(StringComparer.OrdinalIgnoreCase);
DAL.LoadAppSettings("appsettings.json", ds);
DAL.LoadEnvironmentVariable(ds, envs);
Assert.True(ds.ContainsKey("MySqlWithProtected"));
Assert.True(ds.ContainsKey("pgsql"));
var dal = DAL.Create("MySqlWithProtected");
Assert.Equal("KeyOfProtected", dal.ProtectedKey.Secret.ToStr());
var di = ds["pgsql"];
Assert.Equal("pgsql", di.Name);
Assert.Equal("Server=.;Database=master;Uid=root;Pwd=root;provider=PostgreSql", di.ConnectionString);
Assert.Equal("XCode.DataAccessLayer.PostgreSQL", di.Type.FullName);
Assert.Equal("PostgreSql", di.Provider);
}
Assert.Equal("Server=.;Membership=mysql;Uid=root;Pwd=$AES$xpZ49nBk5UscLCFyCx_BUg;provider=mysql", dal.ConnStr);
Assert.Equal("Server=127.0.0.1;Membership=mysql;Uid=root;Pwd=Pass@word;CharSet=utf8mb4;Sslmode=none", dal.Db.ConnectionString);
}
[Fact]
public void LoadEnvironmentVariable()
{
var ds = new ConcurrentDictionary<String, DbInfo>(StringComparer.OrdinalIgnoreCase);
var envs = Environment.GetEnvironmentVariables();
envs.Add("XCode_pgsql", "Server=.;Database=master;Uid=root;Pwd=root;provider=PostgreSql");
DAL.LoadEnvironmentVariable(ds, envs);
Assert.True(ds.ContainsKey("pgsql"));
var di = ds["pgsql"];
Assert.Equal("pgsql", di.Name);
Assert.Equal("Server=.;Database=master;Uid=root;Pwd=root;provider=PostgreSql", di.ConnectionString);
Assert.Equal("XCode.DataAccessLayer.PostgreSQL", di.Type.FullName);
Assert.Equal("PostgreSql", di.Provider);
}
}

View File

@ -1,5 +1,7 @@
{
"Url": "https://newlifex.com/",
//"ProtectedKey": "KeyOfProtected",
"ProtectedKey": "$base64$S2V5T2ZQcm90ZWN0ZWQ",
"ConnectionStrings": {
"sqlite": "Data Source=Data\\Membership.db;provider=sqlite",
"MySQL.AppSettings": {
@ -19,6 +21,7 @@
"TDengine": {
"connectionString": "Server=gz01.newlifex.com;Port=6030;Database=db;user=root;password=taosdata",
"providerName": "TDengine"
}
},
"MySqlWithProtected": "Server=.;Membership=mysql;Uid=root;Pwd=$AES$xpZ49nBk5UscLCFyCx_BUg;provider=mysql"
}
}