于安全考虑几乎每个动态网站都具备IP地址屏蔽功能而网上流传的很多关于该功能的教程大都采用字符串保存和验证IP地址我认为这是不太科学的我试图找到最佳的设计方案
IP地址的长度为位分为段每段位用十进制数字表示每段数字范围为~段与段之间用句点隔开
由此我们了解到IP地址实际上是一个位正整数在C#中可以使用uint类型来表示但SQLServer数据库里好像没有对应的类型转而使用数据库支持的int类型的话则会出现溢出的情况因此我们做出妥协使用long(bigint)类型
TIP:
int取值范围 到
uint取值范围 到
long取值范围 到 那么如何将IP地址转为整数呢?我们看到IPAddress类中有一个[否决的]实例属性Address这个属性的确可以返回一个long值但是测试一下得到的数据确实这样的
>
–>
的确该让它否决这样的整数对我们来说毫无意义我们是无法通过这样的方法比较传入的IP是否介于两个IP值之间的
那么只有自己动手了我们将通过IPAddress类的GetAddressBytes()实例方法获取IP的个段的值然后将它们组合为一个整数下面将提供这个扩展方法
/// <summary>
/// 将IP地址转为整数形式
/// </summary>
/// <returns>整数</returns>
public static long 转换为整数(this IPAddress ip)
{
int x = ;
long o = ;
foreach (byte f in ipGetAddressBytes())
{
o += (long)f << * x;
}
return o;
}
你可以这样使用这个扩展方法
IPAddressParse()转换为整数()
这里还有一个用于逆转换的扩
展方法用于将long转回IPAddress
/// <summary>
/// 将整数转为IP地址
/// </summary>
/// <returns>IP地址</returns>
public static IPAddress 转换为IP地址(this long l)
{
var b = new byte[];
for (int i = ; i < ; i++)
{
b[ i] = (byte)(l >> * i & );
}
return new IPAddress(b);
}
这样我们就可以通过计算得到正确并有意义的整数了
>
–>
OK确立了方案核心下面开始设计SQLServer数据表
这样设计后在添加时将起始和终止IP地址转为long类型并存入并指定一个过期时间
在验证时只需要获取所有未过期的条目比较传入的IP地址是否介于起始值和终止值之间即可
以往通过字符串存储和验证的方案中屏蔽时要么屏蔽一个精确的IP地址要么就屏蔽一段或两段IP如**要想屏蔽到之间的IP的话将会非常麻烦
而我们这样设计就可以轻松实现在数据库里存储的是在数据库中是即使使用肉眼也能极快地判断传入的地址是否介于它们之间更不要说计算机查询了
下面为数据表生成EDM模型
添加IP屏蔽记录的代码
/// <summary>
/// 添加一个新的IP屏蔽区段
/// </summary>
/// <param name=IP区段起始值>起始IP如</param>
/// <param name=IP区段终止值>终止IP如</param>
/// <param name=过期时间>屏蔽截止时间</param>
/// <returns>ID号</returns>
public static Guid 添加(string IP区段起始值 string IP区段终止值 DateTime 过期时间)
{
var id = GuidNewGuid();
var sip = IPAddressParse(IP区段起始值)转换为整数();
var eip = IPAddressParse(IP区段终止值)转换为整数();
using (var c = new SiteMainEntities())
{
//检测是否已存在相同的IP屏蔽记录
var a = cIP地址屏蔽Where(f => f区段起始值 == sip && f区段终止值 == eip);
//如果存在则更新其过期时间
if (aCount()>)
{
var l = aFirst();
if (l过期时间 < 过期时间) l过期时间 = 过期时间;
}
//不存在则正常添加一个新的屏蔽记录
else cAddToIP地址屏蔽(new IP地址屏蔽 { ID = id 过期时间 = 过期时间 区段起始值 = sip 区段终止值 = eip });
cSaveChanges();
}
return id;
}
检测指定IP地址是否
被屏蔽的代码
/// <summary>
/// 检测指定IP地址是否已受到屏蔽
/// </summary>
/// <param name=IP地址>要检测的IP地址</param>
/// <returns>是否属于已屏蔽的IP</returns>
public static bool 检测是否被屏蔽(string IP地址)
{
var ip = IPAddressParse(IP地址)转换为整数();
using (var c = new SiteMainEntities())
{
return cIP地址屏蔽Count(f => f过期时间 > DateTimeNow && ip >= f区段起始值 && ip <= f区段终止值) > ;
}
}
这种方案比起以往的字符串验证方案来说优雅了许多并可以提高数据库查询的效率建议各位在日后的网站开发中都采用此方案