在这篇文章中,我们将探讨如何在 asp.net webforms
中实现ip拦截器,以便在 asmx web 服务方法
和 http 请求
中根据ip地址进行访问控制。我们将使用自定义的 soapextension
和 ihttpmodule
来实现这一功能,并根据常用的两种文本传输协议:soap协议
和 http协议
进行分别讲解。
一、创建asmx接口文件
首先,我们创建一个 asp.net webforms
项目,创建 testasmxproject.asmx
文件,并定义里面的 webservice
服务。
如果不会创建 asmx
文件,可以参考我的上一篇文章:c#进阶-asp.net webforms调用asmx的webservice接口。
testasmxproject.asmx
代码如下:
using system.web.services;
namespace testasmxproject.asmx
{
/// <summary>
/// test 的摘要说明
/// </summary>
[webservice(namespace = "http://tempuri.org/")]
[webservicebinding(conformsto = wsiprofiles.basicprofile1_1)]
[system.componentmodel.toolboxitem(false)]
// 若要允许使用 asp.net ajax 从脚本中调用此 web 服务,请取消注释以下行。
[system.web.script.services.scriptservice]
public class test : system.web.services.webservice
{
[webmethod]
public string helloworld()
{
return "hello world";
}
[webmethod(description = "计算两个数的和")]
public int add(int a, int b)
{
return a + b;
}
}
}
从这个类我们可以看到,目前是有两个 webservice
方法:helloworld
方法和 add
方法。调用 helloworld
方法会返回 "hello world"
字符串;用 add
方法,需要传入 a
和 b
两个参数,会返回 a
和 b
相加的和。
二、基于soap协议的拦截器实现
创建一个自定义的 soapextension
来实现ip拦截。我们还需要一个自定义属性来指定允许的ip地址。
1. 创建ipfilterattribute类
新建 filter
文件夹,再在 filter
文件夹里新建 soap
文件夹。
在 soap
文件夹里创建 ipfilterattribute
类。
ipfilterattribute.cs
代码如下:
using system;
using system.web.services.protocols;
namespace testasmxproject.filter.soap
{
[attributeusage(attributetargets.method)]
public class ipfilterattribute : soapextensionattribute
{
public override type extensiontype => typeof(ipfilter);
public override int priority { get; set; }
public string[] allowedips { get; private set; }
public ipfilterattribute(params string[] ips)
{
allowedips = ips.length > 0 ? ips : null;
priority = 1;
}
}
}
2. 创建基于soapextension的ipfilter注解类
创建 ipfilter.cs
,继承 soapextension
。
ipfilter.cs
代码如下:
using system;
using system.configuration;
using system.linq;
using system.web;
using system.web.services.protocols;
namespace testasmxproject.filter.soap
{
public class ipfilter : soapextension
{
private string[] allowedips;
public override object getinitializer(logicalmethodinfo methodinfo, soapextensionattribute attribute)
{
var ipfilterattribute = attribute as ipfilterattribute;
return ipfilterattribute?.allowedips;
}
public override object getinitializer(type servicetype)
{
return null;
}
public override void initialize(object initializer)
{
allowedips = initializer as string[];
}
public override void processmessage(soapmessage message)
{
switch (message.stage)
{
case soapmessagestage.beforeserialize:
break;
case soapmessagestage.afterserialize:
break;
case soapmessagestage.beforedeserialize:
checkipvalidation(message);
break;
case soapmessagestage.afterdeserialize:
break;
}
}
private void checkipvalidation(soapmessage message)
{
try
{
string clientip = httpcontext.current.request.servervariables["http_x_forwarded_for"];
if (string.isnullorempty(clientip))
{
clientip = httpcontext.current.request.servervariables["remote_addr"];
}
if (!isvalidip(clientip))
{
httpcontext.current.response.clear();
httpcontext.current.response.statuscode = 403;
httpcontext.current.response.contenttype = "text/plain";
httpcontext.current.response.write("access denied: your ip address is not allowed.");
httpcontext.current.response.end();
}
}
catch (exception ex)
{
throw;
}
}
private bool isvalidip(string ip)
{
string configips = configurationmanager.appsettings["ipwhitelist"];
string[] configallowedips = !string.isnullorwhitespace(configips)
? configips.split(new[] { ',' }, stringsplitoptions.removeemptyentries)
: new string[] { };
var allallowedips = (allowedips == null ? new string[] { } : allowedips).concat(configallowedips).toarray();
return allallowedips.any(allowip => ip == allowip);
}
}
}
3. 配置web.config中ip白名单
在 web.config
文件中配置白名单ip列表,在 ipfilter.cs
里我们已经写过该逻辑,web.config
文件中白名单内的 ipwhitelist
里的ip是全局白名单,无论 webservice服务方法
上是否有 [ipfilter]
注解。
<configuration>
<appsettings>
<add key="ipwhitelist" value="127.0.0.1,192.168.1.1" />
</appsettings>
</configuration>
4. 在webservice方法上添加注解
在 webservice服务方法
上使用 [ipfilter]
注解,可以定义该方法的专属ip白名单(包含 web.config
中的全局白名单),如果不设定,则仅使用 web.config
中的白名单。
加了 [ipfilter]
注解后的 testasmxproject.asmx
代码如下:
using system.web.services;
using testasmxproject.filter.soap;
namespace testasmxproject.asmx
{
/// <summary>
/// test 的摘要说明
/// </summary>
[webservice(namespace = "http://tempuri.org/")]
[webservicebinding(conformsto = wsiprofiles.basicprofile1_1)]
[system.componentmodel.toolboxitem(false)]
// 若要允许使用 asp.net ajax 从脚本中调用此 web 服务,请取消注释以下行。
[system.web.script.services.scriptservice]
public class test : system.web.services.webservice
{
[webmethod]
[ipfilter] // 不传入指定ip,使用web.config中的白名单
public string helloworld()
{
return "hello world";
}
[webmethod(description = "计算两个数的和")]
[ipfilter("127.0.0.1", "192.168.1.1")] // 此处为该方法的ip白名单,加上web.config中的白名单共同生效
public int add(int a, int b)
{
return a + b;
}
}
}
三、基于http协议的拦截器实现
接下来,我们创建一个基于http协议的拦截器来实现ip拦截。同样,我们需要一个自定义属性来指定允许的ip地址。
1. 创建ipfilterattribute类
新建 filter
文件夹,再在 filter
文件夹里新建 http
文件夹。
在 soap
文件夹里创建 ipfilterattribute
类。
ipfilterattribute.cs
代码如下:
using system;
using system.configuration;
namespace testasmxproject.filter.http
{
[attributeusage(attributetargets.method, inherited = true, allowmultiple = false)]
public sealed class ipfilterattribute : attribute
{
public string[] allowedips { get; private set; }
public ipfilterattribute(params string[] ips)
{
string configips = configurationmanager.appsettings["ipwhitelist"];
var configallowedips = !string.isnullorempty(configips)
? configips.split(new[] { ',' }, stringsplitoptions.removeemptyentries)
: new string[] { };
allowedips = new string[ips.length + configallowedips.length];
ips.copyto(allowedips, 0);
configallowedips.copyto(allowedips, ips.length);
}
public bool isallowedip(string userip)
{
return allowedips.any(ip => userip == ip);
}
}
}
2. 创建基于ihttpmodule的ipfilter注解类
创建 ipfilter.cs
,继承 ihttpmodule
。
ipfilter.cs
代码如下:
using system;
using system.linq;
using system.reflection;
using system.web;
namespace testasmxproject.filter.http
{
public class ipfilter : ihttpmodule
{
public void init(httpapplication context)
{
context.prerequesthandlerexecute += new eventhandler(onprerequesthandlerexecute);
}
private void onprerequesthandlerexecute(object sender, eventargs e)
{
httpapplication application = (httpapplication)sender;
httpcontext context = application.context;
if (context.request.path.contains(".asmx"))
{
string userip = context.request.userhostaddress;
string methodname = context.request.pathinfo.replace("/", "");
type webservicetype = getwebservicetype(context.request.path);
if (webservicetype != null)
{
methodinfo methodinfo = webservicetype.getmethod(methodname);
if (methodinfo != null)
{
var attribute = methodinfo.getcustomattribute<ipfilterattribute>();
if (attribute != null && !attribute.isallowedip(userip))
{
context.response.statuscode = 403;
context.response.contenttype = "text/plain";
context.response.write("access denied: your ip address is not allowed.");
context.response.end();
}
}
}
}
}
private type getwebservicetype(string path)
{
string servicename = path.split('/')[2].split('.').first();
string namespaceprefix = "";
string typename = $"{namespaceprefix}.{servicename}";
type servicetype = type.gettype(typename);
if (servicetype == null)
{
var assemblies = appdomain.currentdomain.getassemblies();
foreach (var assembly in assemblies)
{
servicetype = assembly.gettype(typename);
if (servicetype != null)
{
break;
}
}
}
return servicetype;
}
public void dispose() { }
}
}
3. 配置web.config中白名单和模块
在 web.config
文件中配置白名单ip列表,在 ipfilter.cs
里我们已经写过该逻辑,web.config
文件中白名单内的 ipwhitelist
里的ip是全局白名单,无论 webservice服务方法
上是否有 [ipfilter]
注解。
<configuration>
<appsettings>
<add key="ipwhitelist" value="127.0.0.1,192.168.1.1" />
</appsettings>
<system.web>
<httpmodules>
<add name="ipfilter" type="testasmxproject.filter.http.ipfilter"/>
</httpmodules>
</system.web>
<system.webserver>
<validation validateintegratedmodeconfiguration="false" />
<modules runallmanagedmodulesforallrequests="true">
<add name="ipfilter" type="testasmxproject.filter.http.ipfilter"/>
</modules>
</system.webserver>
</configuration>
4. 在webservice方法上添加注解
在 webservice服务方法
上使用 [ipfilter]
注解,可以定义该方法的专属ip白名单(包含 web.config
中的全局白名单),如果不设定,则仅使用 web.config
中的白名单。
加了 [ipfilter]
注解后的 testasmxproject.asmx
代码如下:
using system.web.services;
using testasmxproject.filter.http;
namespace testasmxproject.asmx
{
/// <summary>
/// test 的摘要说明
/// </summary>
[webservice(namespace = "http://tempuri.org/")]
[webservicebinding(conformsto = wsiprofiles.basicprofile1_1)]
[system.componentmodel.toolboxitem(false)]
// 若要允许使用 asp.net ajax 从脚本中调用此 web 服务,请取消注释以下行。
[system.web.script.services.scriptservice]
public class test : system.web.services.webservice
{
[webmethod]
[ipfilter] // 不传入指定ip,使用web.config中的白名单
public string helloworld()
{
return "hello world";
}
[webmethod(description = "计算两个数的和")]
[ipfilter("127.0.0.1", "192.168.1.1")] // 此处为该方法的ip白名单,加上web.config中的白名单共同生效
public int add(int a, int b)
{
return a + b;
}
}
}
四、ip拦截器实现总结
通过上述步骤,我们成功实现了在 asp.net webforms
中基于ip地址的访问控制。我们分别使用自定义的 soapextension
和 ihttpmodule
,实现了对asmx web服务方法和http请求的ip拦截。
1. 自定义 soapextension
自定义的 soapextension
通过重载 processmessage
方法,在soap消息处理的不同阶段进行ip地址的验证。通过检查请求的ip地址并与允许的ip列表进行比较,我们可以在消息反序列化之前阻止不符合条件的请求,从而有效地控制对web服务方法的访问。这种方法特别适用于基于soap的web服务,能够在服务方法调用之前进行精细的访问控制。
2. 自定义 ihttpmodule
自定义的 ihttpmodule
通过实现 init
方法并注册 prerequesthandlerexecute
事件,在每个http请求处理之前执行ip地址验证。通过反射获取请求对应的方法,并检查方法上的自定义属性 ipfilterattribute
,我们可以动态地对特定方法应用ip过滤规则。结合 web.config
中配置的白名单ip地址,这种方法能够灵活地扩展和维护ip访问控制规则,适用于一般的http请求拦截需求。
3. 本文提供方法的实现优势
这种ip拦截器的实现方法不仅增强了应用程序的安全性,还具有良好的扩展性和可维护性。开发者可以根据具体需求,通过配置文件或代码注解灵活地管理允许的ip地址。通过将安全控制逻辑封装在自定义模块和扩展中,可以保持业务代码的简洁和可读性。
希望这篇文章对你在asp.net webforms应用中的ip访问控制有所帮助。
发表评论