App.config中的数据库连接字符串,是在Settings.settings同步过来的。后者在设计时支持数据集DataSet的设计,但运行时不能更改,可以更改App.config的连接字符串,但无法加密、解密。造成的结果是数据库连接字符串,要么固定不变,要么以明文显示,极不安全。多次尝试后,成功的解决这些问题。
我的经验:
1. Settings.settings中的设置和App.config中的设置会相互更新,彼此同步。前者用于设计支持,后者用于运行时。可以手工阻止两者同步,使两者设置不一样,如果没有发布App.config,则全部采用Settings.settings的设置;否则在运行时App.config会覆盖Settings.settings的设置。
2. Settings.settings中的Application范围设置只在设计时更改,是只读的属性,User范围设置可以在运行时读写。参考MSDN文章“在 C# 中使用设置”中的说明:
应用程序作用域设置与用户作用域设置之间的重要区别是,用户作用域设置在运行时为读/写,并且可在代码中对其值进行更改和保存。应用程序作用域设置在运行时为只读。虽然可以读取,但是不能对其进行写入。具有应用程序作用域的设置只能在设计时或通过手动修改设置文件进行更改。
3. Settings中的只有“连接字符串”设置才能被数据集DataSet设计时支持,而“连接字符串”设置只能是Application范围设置,是只读的属性。
解决思路:
将Settings.settings的设计时代码文件Settings.Designer.cs内容复制到Setgings.cs中,删除Settings.Designer.cs,更改部分代码。连接字符串在Setgings.cs中缺省设置为明码,在App.config中为加密码。代码如下,DESEncrypt为静态加密函数,DESDecrypt为静态解密函数:
1using System.Xml;
2namespace A.Properties
3{
4
5 [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
6 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "8.0.0.0")]
7 internal sealed class Settings : global::System.Configuration.ApplicationSettingsBase
8 {
9
10 private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
11
12 public static Settings Default
13 {
14 get
15 {
16 return defaultInstance;
17 }
18 }
19
20 [global::System.Configuration.ApplicationScopedSettingAttribute()]
21 [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
22 [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)]
23 [global::System.Configuration.DefaultSettingValueAttribute("Data Source=(local);Initial Catalog=Test;Persist Security Info=True;User ID=sa;Password=")]
24 public string ConnectionString1
25 {
26 get
27 {
28 return Encrypt.DESDecrypt(this["ConnectionString1"].ToString());
29 }
30 set
31 {
32 base["ConnectionString1"] = Encrypt.DESEncrypt(value);
33 this.SetKeyValue("A.Properties.Settings.ConnectionString1", Encrypt.DESEncrypt(value));
34 }
35 }
36
37
38 /**//// <summary>
39 /// 保存设置
40 /// </summary>
41 /// <param name="AppKey"></param>
42 /// <param name="AppValue"></param>
43 private void SetKeyValue(string AppKey, string AppValue)
44 {
45 XmlDocument xDoc = new XmlDocument();
46 xDoc.Load(System.Windows.Forms.Application.ExecutablePath + ".config");
47
48 XmlNode xNode;
49 XmlElement xElem1;
50 XmlElement xElem2;
51
52 xNode = xDoc.SelectSingleNode("//connectionStrings");
53
54 xElem1 = (XmlElement)xNode.SelectSingleNode("//add[@name='" + AppKey + "']");
55 if (xElem1 != null) xElem1.SetAttribute("connectionString", AppValue);
56 else
57 {
58 xElem2 = xDoc.CreateElement("add");
59 xElem2.SetAttribute("name", AppKey);
60 xElem2.SetAttribute("connectionString", AppValue);
61 xNode.AppendChild(xElem2);
62 }
63 xDoc.Save(System.Windows.Forms.Application.ExecutablePath + ".config");
64 }
65
66
67 }
68}
69
默认情况下,新的连接字符串设置只在重启时生效。但可以操作数据前,加上一条更改Dataset中Adapter的连接字符串的语句,就可以不用重启,代码如下:
1 private void toolStripButton1_Click(object sender, EventArgs e)
2 {
3 A.Properties.Settings.Default.ConnectionString1 = @"Data Source=(local);Initial Catalog=Test2;Persist Security Info=True;User ID=sa";
4
5 this.toolStripTextBox1.Text = A.Properties.Settings.Default.ConnectionString1;
6 this.Test1TableAdapter.Connection.ConnectionString = A.Properties.Settings.Default.ConnectionString1;
7 MessageBox.Show(A.Properties.Settings.Default.ConnectionString1);
8 this.Test1TableAdapter.Fill(this.dataSet1.Test1);
9
10 }
数据库技术就是一坛陈年老酒,越久越香,学以致用。
评论
#4楼 [楼主] 回复 引用 查看
To Zhouqi,修改Properties.Settings类,加入加密解密,关键代码如下
public string ConnectionString1
{
get
{
return Encrypt.DESDecrypt(this["ConnectionString1"].ToString());
}
set
{
base["ConnectionString1"] = Encrypt.DESEncrypt(value);
this.SetKeyValue("A.Properties.Settings.ConnectionString1", Encrypt.DESEncrypt(value));
}
}
#5楼 回复 引用
这个可以移植吗?就是说我生成的setup文件安装在别的机子上访问的是同一个服务器,可以正常运行吗?我的邮箱zhouqi1234511111@sina.com.cn期待你的答案,谢谢!#7楼 回复 引用
同样是三楼问题,如果数据库服务器密码变化了,相当于连接字符串更改了。怎么移植。我简单的看了一下搂主的代码,好像是根据Settings.settings中明文连接字符串,动态生成加密后的写入到APP.config中了,是这样么。
那这样的话三楼的问题就不能解决了。
除非每次变动一次数据库连接密码或用户名,就需要重新打包一次。。。
不可行。。。
不过可以参见用reg-iis对配置文件进行加密。。。
我的邮箱:liuzhitian83@126.com
qq:81640062
不知道我说的对否,有问题及时联系,大家一起探讨。。。
#8楼 [楼主] 回复 引用 查看
再解释一下:
1. 引用Microsoft.Data.ConnectionUI.dll和Microsoft.Data.ConnectionUI.Dialog.dll后,引用using Microsoft.Data.ConnectionUI命名空间,使用DataConnectionDialog类可以调用MS的标准数据库连接配置对话框,解决保存到ConnectionString1中。
当然,你也可以自建一个窗口来读写ConnectionString1值。
2. 配置类的属性ConnectionString1,可以get解密后的字符串,也可以set加密后的字符串,SetKeyValue过程可以保存加密后的字符串(通过读写XML文档实现),Encrypt.DESEncrypt和Encrypt.DESDecrypt是我写的加密和解密函数。