| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 579 人关注过本帖
标题:[转载]用DES加密数据库信息,增强Asp.net的安全性
只看楼主 加入收藏
hap163
Rank: 1
等 级:新手上路
帖 子:24
专家分:0
注 册:2005-11-9
收藏
 问题点数:0 回复次数:3 
[转载]用DES加密数据库信息,增强Asp.net的安全性

刚在网上找了一些跟数据库连接的资料....想想这个应该对一些人是有帮助的...所以就贴上来了....


在实际的数据库系统开发中,我们一般都是把数据库的连接字符串放在了Asp.net的配置文件Config.web中,连接字符串一般如下:
<appSettings>
<add key="ConnStr" value="server=192.168.2.36;database=Mdata;uid=sa;password=111000" />
</appSettings>

然后在程序中通过System.Configuration.ConfigurationSettings.AppSettings ["ConnStr"]方法读取,这样大大提高了程序访问的方便性.但这种方法也潜在产生了数据库的安全隐患,因为只要能读到这个配置文件的用户,稍懂计算机知识的人立即就能知道数据库的登陆信息,并进行登录数据库,从而进行各种操作.虽然服务器有许多安全设置,但是,现在的网络安全还不是十分可靠,在安全方面还是需要多下点功夫.如果我们能将配置文件写入以下的值:
<appSettings>
<add key="ConnStr" value="22C25FEE2659A7F295EB4963AAAE2D5474AA434036CC43892128E73112BC2B2C568E8D3F53EFC7AC0F6413627D9D036E43A3E57C1953E21167760D12E59E3443" />
</appSettings>
就算是网站源码被黑客下载了,或者web服务器上的配置文件泄露了出去,但是这段代码又有谁能译出原文是什么呢?

中间这些无规律的字符可不是简单的字节转换得到的.我们完全可以借助.net提供的强大安全功能实现DES加密数据库连接信息.
对于DES加密的理论这里就不多讲了,可以查阅其它资料,我们这里只讲它的实现应用.如果朋友们能理解并且相信它的加密强度:那么下边我就给大家多费点口舌谈一谈具体的实现方法:

首先,我们为了开发及以后布署的方便性,我们最好能写一个小的工具进行加密及配置文件的写入,因为数据库布署在不同的机器上连接字符串改一次,如果手工生成加密字符串再修改配置文件,这种重复的劳动,可能过段时间,你就会受不了,所以我们找一个一劳永逸的方法(有点夸张,没有什么东西能一劳永逸哟),自己动手写个小工具.我也试找个地方把工具及实现方法上传至网上,如果有兴趣的朋友,可以看看.
先看看加解密的关键方法:加密方法及解密方法如下,如果不能完全不能明白这两个方法也不影响我们的使用:我们只需要知道调用规则即可
#region 加密方法
//pToEncrypt为需要加密字符串,sKey为密钥
public string Encrypt(string pToEncrypt, string sKey)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
//把字符串放到byte数组中
//原来使用的UTF8编码,我改成Unicode编码了,不行
byte[] inputByteArray = Encoding.Default.GetBytes(pToEncrypt);

//建立加密对象的密钥和偏移量
//使得输入密码必须输入英文文本
des.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(),CryptoStreamMode.Write);

cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
StringBuilder ret = new StringBuilder();
foreach(byte b in ms.ToArray())
{
ret.AppendFormat("{0:X2}", b);
}
ret.ToString();
return ret.ToString();
}
#endregion
#region 解密方法
//pToDecrypt为需要解密字符串,sKey为密钥
public string Decrypt(string pToDecrypt, string sKey)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
byte[] inputByteArray = new byte[pToDecrypt.Length / 2];
for(int x = 0; x < pToDecrypt.Length / 2; x++)
{
int i = (Convert.ToInt32(pToDecrypt.Substring(x * 2, 2), 16));
inputByteArray[x] = (byte)i;
}

//建立加密对象的密钥和偏移量,此值重要,不能修改
des.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(),CryptoStreamMode.Write);
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
//建立StringBuild对象,CreateDecrypt使用的是流对象,必须把解密后的文本变成流对象
StringBuilder ret = new StringBuilder();
return System.Text.Encoding.Default.GetString(ms.ToArray());
}
#endregion
注意,sKey为一个长度为八位的字符串,加密和解密时必须一致.为了加强模糊度,我们可以在程序初始化时用不可打印ascii码组合连接得到sKey,如下所示:
int[] tmp=new int[8]{23,234,195,165,201,240,143,198};
foreach(int i in tmp)
{
sKey+=((char)i).ToString();
}
利用我们写的工具进行配置文件的加解密(该工具中也涉及到一些xml操作方法),既可读出原来的配置信息,又可以改写或者添加配置节.有一处我们还要注意,就是读取原来的配置信息时,不能让密码信息呈现出来,也不能呈现为"******"的掩码形式,网上好多的工具软件,比如密码查看器,只要往密码框上一放.密码原文立即暴露无遗.而我们要做到用这类软件查看到的还是一些"******"字符,但是在用户对读出密码不做修改时继续保持原来的密码.那么该怎么实现呢.这点小技巧,留给朋友们在代码中找找看吧.
下面说说我们在web程序中该怎样使用加密后的连接信息呢,也很简单,用原来的方法读取配置文件ConnStr节信息后,再进行上面方法解密,当然解密用的密钥也必须和加密时一致.解密成功后就ok了!
代码如下:
string strconn = System.Configuration.ConfigurationSettings.AppSettings["ConnStr"];
connstr = Decrypt(strconn,sKey);
conn=new SqlConnection(connstr);
这些代码各位一看就会,我就不唾沫乱飞了吧.


搜索更多相关主题的帖子: 数据库 DES Asp 
2006-03-24 22:50
hap163
Rank: 1
等 级:新手上路
帖 子:24
专家分:0
注 册:2005-11-9
收藏
得分:0 

实现原码(1)

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Text;
using System.Xml;
using System.Security.Cryptography;

namespace writeconfig
{
/// <summary>
/// Form1 的摘要说明。
/// </summary>
public class frmMain : System.Windows.Forms.Form
{
private System.Windows.Forms.Label labserver;
private System.Windows.Forms.Label labdatabase;
private System.Windows.Forms.Label labuser;
private System.Windows.Forms.Label labpassword;
private System.Windows.Forms.TextBox txtserver;
private System.Windows.Forms.TextBox txtdatabase;
private System.Windows.Forms.TextBox txtuser;
private System.Windows.Forms.TextBox txtpassword;
private System.Windows.Forms.Button btnRead;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox txtfile;
private System.Windows.Forms.Button btnWrite;
private System.Windows.Forms.Button btnLook;
private System.Windows.Forms.OpenFileDialog ofd;
private System.Windows.Forms.Label labkey;
private System.Windows.Forms.TextBox txtkey;
private FileInfo fi;
private bool Find;
private string password="";
protected string keypassword="";
private System.Windows.Forms.Button btnDeleteNode;

/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null;

public frmMain()
{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();
int[] tmp=new int[8]{23,234,195,165,201,240,143,198};
foreach(int i in tmp)
{
keypassword+=((char)i).ToString();
}
}

/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
{
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(frmMain));
this.labserver = new System.Windows.Forms.Label();
this.labdatabase = new System.Windows.Forms.Label();
this.labuser = new System.Windows.Forms.Label();
this.labpassword = new System.Windows.Forms.Label();
this.txtserver = new System.Windows.Forms.TextBox();
this.txtdatabase = new System.Windows.Forms.TextBox();
this.txtuser = new System.Windows.Forms.TextBox();
this.txtpassword = new System.Windows.Forms.TextBox();
this.btnRead = new System.Windows.Forms.Button();
this.btnWrite = new System.Windows.Forms.Button();
this.txtfile = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.btnLook = new System.Windows.Forms.Button();
this.ofd = new System.Windows.Forms.OpenFileDialog();
this.labkey = new System.Windows.Forms.Label();
this.txtkey = new System.Windows.Forms.TextBox();
this.btnDeleteNode = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// labserver
//
this.labserver.Location = new System.Drawing.Point(21, 57);
this.labserver.Name = "labserver";
this.labserver.Size = new System.Drawing.Size(87, 14);
this.labserver.TabIndex = 0;
this.labserver.Text = "服务器:";
//
// labdatabase
//
this.labdatabase.Location = new System.Drawing.Point(21, 93);
this.labdatabase.Name = "labdatabase";
this.labdatabase.Size = new System.Drawing.Size(87, 14);
this.labdatabase.TabIndex = 1;
this.labdatabase.Text = "数据源:";
//
// labuser
//
this.labuser.Location = new System.Drawing.Point(21, 129);
this.labuser.Name = "labuser";
this.labuser.Size = new System.Drawing.Size(87, 14);
this.labuser.TabIndex = 2;
this.labuser.Text = "用户名:";
//
// labpassword
//
this.labpassword.Location = new System.Drawing.Point(21, 165);
this.labpassword.Name = "labpassword";
this.labpassword.Size = new System.Drawing.Size(87, 14);
this.labpassword.TabIndex = 3;
this.labpassword.Text = "密 码:";
//
// txtserver
//
this.txtserver.Location = new System.Drawing.Point(117, 50);
this.txtserver.Name = "txtserver";
this.txtserver.Size = new System.Drawing.Size(187, 25);
this.txtserver.TabIndex = 4;
this.txtserver.Text = "";
//
// txtdatabase
//
this.txtdatabase.Location = new System.Drawing.Point(117, 86);
this.txtdatabase.Name = "txtdatabase";
this.txtdatabase.Size = new System.Drawing.Size(187, 25);
this.txtdatabase.TabIndex = 4;
this.txtdatabase.Text = "";
//
// txtuser
//
this.txtuser.Location = new System.Drawing.Point(117, 122);
this.txtuser.Name = "txtuser";
this.txtuser.Size = new System.Drawing.Size(187, 25);
this.txtuser.TabIndex = 4;
this.txtuser.Text = "";
//
// txtpassword
//
this.txtpassword.Location = new System.Drawing.Point(117, 158);
this.txtpassword.Name = "txtpassword";
this.txtpassword.PasswordChar = '*';
this.txtpassword.Size = new System.Drawing.Size(187, 25);
this.txtpassword.TabIndex = 4;
this.txtpassword.Text = "";
this.txtpassword.TextChanged += new System.EventHandler(this.txtpassword_TextChanged);
//
// btnRead
//
this.btnRead.Location = new System.Drawing.Point(320, 154);
this.btnRead.Name = "btnRead";
this.btnRead.Size = new System.Drawing.Size(100, 29);
this.btnRead.TabIndex = 5;
this.btnRead.Text = "读配置";
this.btnRead.Click += new System.EventHandler(this.btnRead_Click);
//
// btnWrite
//
this.btnWrite.Location = new System.Drawing.Point(320, 191);
this.btnWrite.Name = "btnWrite";
this.btnWrite.Size = new System.Drawing.Size(100, 28);
this.btnWrite.TabIndex = 5;
this.btnWrite.Text = "写配置";
this.btnWrite.Click += new System.EventHandler(this.btnWrite_Click);
//
// txtfile
//
this.txtfile.Location = new System.Drawing.Point(117, 14);
this.txtfile.Name = "txtfile";
this.txtfile.Size = new System.Drawing.Size(187, 25);
this.txtfile.TabIndex = 6;
this.txtfile.Text = "请选择目录下Web.config文件";
//
// label1
//
this.label1.Location = new System.Drawing.Point(21, 21);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(96, 14);
this.label1.TabIndex = 3;
this.label1.Text = "配置文件:";
//
// btnLook
//
this.btnLook.Location = new System.Drawing.Point(320, 13);
this.btnLook.Name = "btnLook";
this.btnLook.Size = new System.Drawing.Size(100, 28);
this.btnLook.TabIndex = 7;
this.btnLook.Text = "浏览...";
this.btnLook.Click += new System.EventHandler(this.btnLook_Click);
//
// ofd
//
this.ofd.Filter = "Web配置文件|web.config";
//
// labkey
//
this.labkey.Location = new System.Drawing.Point(21, 201);
this.labkey.Name = "labkey";
this.labkey.Size = new System.Drawing.Size(96, 14);
this.labkey.TabIndex = 3;
this.labkey.Text = "读写节值:";
//
// txtkey
//
this.txtkey.Location = new System.Drawing.Point(117, 194);
this.txtkey.Name = "txtkey";
this.txtkey.Size = new System.Drawing.Size(187, 25);
this.txtkey.TabIndex = 4;
this.txtkey.Text = "ConnStr";
//
// btnDeleteNode
//
this.btnDeleteNode.Location = new System.Drawing.Point(320, 119);
this.btnDeleteNode.Name = "btnDeleteNode";
this.btnDeleteNode.Size = new System.Drawing.Size(100, 28);
this.btnDeleteNode.TabIndex = 5;
this.btnDeleteNode.Text = "删除节";
this.btnDeleteNode.Visible = false;
this.btnDeleteNode.Click += new System.EventHandler(this.btnDeleteNode_Click);
//
// frmMain
//
this.AutoScaleBaseSize = new System.Drawing.Size(8, 18);
this.ClientSize = new System.Drawing.Size(437, 239);
this.Controls.Add(this.btnLook);
this.Controls.Add(this.txtfile);
this.Controls.Add(this.btnRead);
this.Controls.Add(this.txtserver);
this.Controls.Add(this.labpassword);
this.Controls.Add(this.labuser);
this.Controls.Add(this.labdatabase);
this.Controls.Add(this.labserver);
this.Controls.Add(this.txtdatabase);
this.Controls.Add(this.txtuser);
this.Controls.Add(this.txtpassword);
this.Controls.Add(this.btnWrite);
this.Controls.Add(this.label1);
this.Controls.Add(this.labkey);
this.Controls.Add(this.txtkey);
this.Controls.Add(this.btnDeleteNode);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "frmMain";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "数据库连接配置";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);

}
#endregion

2006-03-24 22:51
hap163
Rank: 1
等 级:新手上路
帖 子:24
专家分:0
注 册:2005-11-9
收藏
得分:0 
实现原码(2)

/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new frmMain());
}

private void Form1_Load(object sender, System.EventArgs e)
{

}

private void btnLook_Click(object sender, System.EventArgs e)
{
ofd.ShowDialog();
txtfile.Text=ofd.FileName;
}

private bool inittxtfile()
{
txtfile.Text=txtfile.Text.Trim();
if(txtfile.Text=="")
{
txtfile.Text=System.Environment.CurrentDirectory+@"\web.config";
}
fi=new FileInfo(txtfile.Text);
if(!fi.Exists)
{
MessageBox.Show("没有找到配置文件!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
return false;
}
else
{
return true;
}

}
private void btnWrite_Click(object sender, System.EventArgs e)
{
if(!inittxtfile())
{
return;
}
if((txtserver.Text.Trim()=="")||(txtdatabase.Text.Trim()=="")||(txtuser.Text.Trim()==""))
{
MessageBox.Show("写配置失败,配置信息不完整!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
return;
}
Find=false;
XmlDocument xd=new XmlDocument();
xd.Load(fi.FullName);
XmlNodeList nodeList=null;
try
{
nodeList=xd.SelectSingleNode("/configuration/appSettings").ChildNodes; //获取appSettings节点的所有子节点
}
catch
{
MessageBox.Show("写配置失败,未找到配置节!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
return;
}
//连接字符串
string orgstr="server="+txtserver.Text.Trim()+";uid="+txtuser.Text.Trim()+";database="+txtdatabase.Text.Trim()+";pwd="+password+"";
string str= Encrypt(orgstr,keypassword);

foreach(XmlNode Node in nodeList)
{
XmlElement xe=(XmlElement)Node; //将子节点类型转换为XmlElement类型
if(xe.GetAttribute("key")==txtkey.Text.Trim())
{
xe.Attributes["value"].Value = str;
Find=true;
break;
}
}
if(Find)
{
xd.Save(txtfile.Text);
MessageBox.Show("写配置成功!","配置成功",MessageBoxButtons.OK,MessageBoxIcon.Information);
}
else
{
//未找到节点,建立节点
XmlNode xn=xd.SelectSingleNode("/configuration/appSettings");
XmlElement elem = xd.CreateElement("and");
elem.SetAttribute("key",txtkey.Text.Trim());
elem.SetAttribute("value",str);
try
{
xn.AppendChild(elem);
xd.Save(fi.FullName);
MessageBox.Show("写配置成功!","配置成功",MessageBoxButtons.OK,MessageBoxIcon.Information);
}
catch
{
MessageBox.Show("写配置失败,可能文件权限不够!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
}
}

private void btnRead_Click(object sender, System.EventArgs e)
{
if(!inittxtfile())
{
return;
}
Find=false;
XmlDocument xd=new XmlDocument();
xd.Load(fi.FullName);
XmlNodeList nodeList=null;
try
{
nodeList=xd.SelectSingleNode("/configuration/appSettings").ChildNodes; //获取appSettings节点的所有子节点
}
catch
{
MessageBox.Show("读配置失败,未找到配置节!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
return;
}
string readstr="";
foreach(XmlNode Node in nodeList)
{
XmlElement xe=(XmlElement)Node; //将子节点类型转换为XmlElement类型
if(xe.GetAttribute("key")==txtkey.Text.Trim())
{
readstr=xe.Attributes["value"].Value;
Find=true;
break;
}
}
if(!Find)
{
MessageBox.Show("读配置失败,未找到配置节!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
return;
}
#region 读配置并解析
string connstr=Decrypt(readstr,keypassword);
string [] tmp2=connstr.Split(new char[]{';','='});
for(int i=0;i<tmp2.Length;i+=2)
{
if((tmp2[i].ToLower()=="server")||(tmp2[i].ToLower()=="data source"))
{
txtserver.Text=tmp2[i+1];
}
else if((tmp2[i].ToLower()=="uid")||(tmp2[i].ToLower()=="userid"))
{
txtuser.Text=tmp2[i+1];
}
else if((tmp2[i].ToLower()=="database")||(tmp2[i].ToLower()=="initial catalog"))
{
txtdatabase.Text=tmp2[i+1];
}
else if((tmp2[i].ToLower()=="pwd")||(tmp2[i].ToLower()=="password"))
{
txtpassword.Text="*******************";
password=tmp2[i+1];
}
continue;

}
#endregion
}
#region 加密方法
public string Encrypt(string pToEncrypt, string sKey)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
byte[] inputByteArray = Encoding.Default.GetBytes(pToEncrypt);

//建立加密对象的密钥和偏移量
//原文使用ASCIIEncoding.ASCII方法的GetBytes方法
//使得输入密码必须输入英文文本
des.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(),CryptoStreamMode.Write);
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
StringBuilder ret = new StringBuilder();
foreach(byte b in ms.ToArray())
{
ret.AppendFormat("{0:X2}", b);
}
ret.ToString();
return ret.ToString();
}
#endregion
#region 解密方法
public string Decrypt(string pToDecrypt, string sKey)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();

byte[] inputByteArray = new byte[pToDecrypt.Length / 2];
for(int x = 0; x < pToDecrypt.Length / 2; x++)
{
int i = (Convert.ToInt32(pToDecrypt.Substring(x * 2, 2), 16));
inputByteArray[x] = (byte)i;
}

//建立加密对象的密钥和偏移量,此值重要,不能修改
des.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(),CryptoStreamMode.Write);

cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();


//建立StringBuild对象,CreateDecrypt使用的是流对象,必须把解密后的文本变成流对象
StringBuilder ret = new StringBuilder();

return System.Text.Encoding.Default.GetString(ms.ToArray());
}
#endregion
private void txtpassword_TextChanged(object sender, System.EventArgs e)
{
password=txtpassword.Text.Trim();
}

private void btnDeleteNode_Click(object sender, System.EventArgs e)
{
if(!inittxtfile())
{
return;
}
XmlDocument xd=new XmlDocument();
xd.Load(fi.FullName);

XmlNodeList nodeList=null;
try
{
nodeList=xd.SelectSingleNode("/configuration/appSettings").ChildNodes; //获取appSettings节点的所有子节点
}
catch
{
MessageBox.Show("删除配置节失败,未找到配置节!","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
return;
}
int ClearNodes=0;
foreach(XmlNode Node in nodeList)
{
XmlElement xe=(XmlElement)Node; //将子节点类型转换为XmlElement类型
if(xe.GetAttribute("key")==txtkey.Text.Trim())
{
Node.RemoveAll();
ClearNodes++;
}
}
if(ClearNodes>0)
{
xd.Save(txtfile.Text);
MessageBox.Show("删除配置节成功!","删除成功",MessageBoxButtons.OK,MessageBoxIcon.Information);
}
}
}
}

2006-03-24 22:52
znan
Rank: 1
等 级:新手上路
帖 子:37
专家分:0
注 册:2006-3-19
收藏
得分:0 
谢谢你的转载!
我觉得你太用心良苦!
2006-03-25 17:17
快速回复:[转载]用DES加密数据库信息,增强Asp.net的安全性
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.016012 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved