http://www.aspxcs.net/HTML/0942423489.html#
相信很多开发者都用过WebService来实现程序的面向服务,本文主要介绍WebService的身份识别实现方式,当然本文会提供一个不是很完善的例子,权当抱砖引玉了.
首先我们来介绍webservice下的两种验证方式,
一.通过集成windows身份验证
通过集成windows方式解决webservice的安全问题是一个很简洁,并且行之有效的解决方案,该方案的优 点是比较安全,性能较好,当然因为与windows紧密的结合到了一起,缺点自然也很明显了,第一,不便于移植,第二,要进行相关的配置部署工作(当然我 们也可以用代码来操作IIS,只不过比较麻烦,最近一直做自动化部署,所以一讲到配置马上就会联想到怎么去自动部署)
具体怎么做呢?
服务器端:配置IIS虚拟目录为集成windows身份验证
客户端:
- Service1?wr?=?new?Service1();??
- ?
- wr.Credentials?=?new?NetworkCredential("administrator","123");??
- ?
- lblTest.Text?=?wr.Add(2,2).ToString();??
?
二.使用 SoapHeader(SOAP 标头)自定义身份验证
SoapHeader 多数情况下用来传递用户身份验证信息,当然它的作用远不止如此,有待于在实际应用中发掘,体可以实现哪些东西大家有想法可以留言一起交流.
SoapHeader 使用步骤:
(1) 创建继承自 System.Web.WebServices.SoapHeader 的自定义 SoapHeader 类型。
(2) 在 WebService 中创建拥有 public 访问权限的自定义 SoapHeader 字段。
(3) 在需要使用 SoapHeader 的 WebMethod 上添加 SoapHeaderAttribute 访问特性。SoapHeaderAttribute 构造必须指定 memberName 参数,就是我们在第二步中申明的字段名称。
(4) 生成器会自动为客户端生成同名的自定义 SoapHeader 类型,只不过比起我们在 WebService 端创建的要复杂一些。同时还会为代理类型添加一个 soapheaderValue 属性。
下面展示一段SoapHeader的代码,多余的方法将会在后面用到
客户端
- class?Program?
- ????{?
- ????????static?void?Main(string[]?args)?
- ????????{?
- ????????????Service1?ws?=?new?Service1();?
- ????????????ServiceCredential?mycredential?=?new?ServiceCredential();?
- ?
- ????????????mycredential.User?=?"gazi";?
- ????????????mycredential.Password="gazi";?
- ????????????ws.ServiceCredentialValue?=?mycredential;?
- ????????????string??mystr=ws.SayHello();?????????????
- ????????}?
- ????}?
服务器端
- public?class?Service1?:?System.Web.Services.WebService?
- ????{?
- ????????public?ServiceCredential?myCredential;?
- ?
- ????????[WebMethod]?
- ????????[SoapHeader("myCredential",?Direction?=?SoapHeaderDirection.In)]?
- ????????public?string??SayHello()??
- ????????{??
- ????????????return?"hello";?
- ????????}?
- ????}?
- ?
- ?
- ?
- public?class?ServiceCredential?:?SoapHeader?
- ????{?
- ????????public?string?User;?
- ????????public?string?Password;?
- ????????public?static?bool?ValideUser(string?User,string?Password)?
- ????????{?
- ????????????return?true;?
- ????????}?
- ????????public?static?void?CheckUser(Object?sender,?WebServiceAuthenticationEvent?e)?
- ????????{?
- ????????????if?(ValideUser(e.User,?e.Password))?
- ????????????{?
- ????????????????return;?
- ????????????}?
- ????????????else?
- ????????????{?
- ????????????????WebServiceAuthenticationModule?module?=?sender?as?WebServiceAuthenticationModule;?
- ????????????????module.Result.AddRule("验证错误",?"不能确认您的身份,请检查用户名和密码");?
- ????????????}?
- ????????}?
- ????}?
?
当我们拥有很多个类的时候,要添加一个或者删除一个验证方式(假设需要进行多种认证)是非常麻烦的,我们不可能跑到 每个方法里面去加一个方法调用,这是灾难性的工作,当然我们也可以用AOP来实现,Aop的话需要额外增加很多代码或者直接引入第三方来做,但是我们可不 可以有更简便的方法呢?
OK,答案就是使用HttpModule,我们集成IHttpModule写一个处理模块,那么它的原理是什么呢?具体进行了哪些操作呢?我们的思路如下:
-
HTTP Module 分析 HTTP 消息以检查它们是不是 SOAP 消息。
- 如果 HTTP Module 检测到 SOAP 消息,它会读取 SOAP 标头。
- 如果 SOAP 消息的 SOAP 标头中有身份验证凭据,HTTP Module 将引发一个自定义 global.asax 事件。
下面来看看我们的Module代码
- public?class?WebServiceAuthenticationModule?:?IHttpModule?
- ????{?
- ????????private?static?WebServiceAuthenticationEventHandler?
- ??????????????????????_eventHandler?=?null;?
- ?????????
- ?????????
- ?????????
- ????????public?static?event?WebServiceAuthenticationEventHandler?Authenticate?
- ????????{?
- ????????????add?{?_eventHandler?+=?value;?}?
- ????????????remove?{?_eventHandler?-=?value;?}?
- ????????}?
- ????????public?Result?Result?=?new?Result();?
- ?
- ????????public?void?Dispose()?
- ????????{?
- ????????}?
- ????????public?void?Init(HttpApplication?app)?
- ????????{?
- ????????????app.AuthenticateRequest?+=?new?
- ???????????????????????EventHandler(this.OnEnter);?
- ????????????Result.EndValid?+=?new??
- ????????????????EventHandler(this.OnCheckError);?
- ????????}?
- ?
- ?????????
- ?????????
- ?????????
- ?????????
- ????????private?void?OnAuthenticate(WebServiceAuthenticationEvent?e)?
- ????????{?
- ????????????if?(_eventHandler?==?null)?
- ????????????????return;?
- ?
- ????????????_eventHandler(this,?e);?
- ????????????if?(e.User?!=?null)?
- ????????????????e.Context.User?=?e.Principal;?
- ????????}?
- ?
- ????????public?string?ModuleName?
- ????????{?
- ????????????get?{?return?"WebServiceAuthentication";?}?
- ????????}?
- ?
- ????????void?OnEnter(Object?source,?EventArgs?eventArgs)?
- ????????{?
- ????????????HttpApplication?app?=?(HttpApplication)source;?
- ????????????HttpContext?context?=?app.Context;?
- ????????????Stream?HttpStream?=?context.Request.InputStream;?
- ?
- ?????????????
- ????????????long?posStream?=?HttpStream.Position;?
- ?
- ?????????????
- ?????????????
- ????????????if?(context.Request.ServerVariables["HTTP_SOAPACTION"]?==?null)?
- ????????????????return;?
- ?
- ?????????????
- ?????????????
- ????????????XmlDocument?dom?=?new?XmlDocument();?
- ????????????string?soapUser;?
- ????????????string?soapPassword;?
- ?
- ????????????try?
- ????????????{?
- ????????????????dom.Load(HttpStream);?
- ?
- ?????????????????
- ????????????????HttpStream.Position?=?posStream;?
- ?
- ?????????????????
- ????????????????soapUser?=?
- ????????????????????dom.GetElementsByTagName("User").Item(0).InnerText;?
- ????????????????soapPassword?=?
- ????????????????????dom.GetElementsByTagName("Password").Item(0).InnerText;?
- ????????????}?
- ????????????catch?(Exception?e)?
- ????????????{?
- ?????????????????
- ????????????????HttpStream.Position?=?posStream;?
- ?
- ?????????????????
- ????????????????XmlQualifiedName?name?=?new?
- ?????????????????????????????XmlQualifiedName("Load");?
- ????????????????SoapException?soapException?=?new?SoapException(?
- ??????????????????????????"SOAP请求没有包含必须的身份识别信息",?name,?e);?
- ????????????????throw?soapException;?
- ????????????}?
- ?????????????
- ????????????OnAuthenticate(new?WebServiceAuthenticationEvent?
- ?????????????????????????(context,?soapUser,?soapPassword));?
- ????????????Result.OnEndValid();?
- ????????????return;?
- ????????}?
- ????????void?OnCheckError(Object?sender,?EventArgs?e)?
- ????????{?
- ????????????if?(Result.BrokenRules.Count?==?0)?
- ????????????{?
- ????????????????return;?
- ????????????}?
- ????????????else?
- ????????????{?
- ????????????????HttpApplication?app?=?HttpContext.Current.ApplicationInstance;?
- ????????????????app.CompleteRequest();?
- ????????????????app.Context.Response.Write(Result.Error);?
- ????????????}?
- ????????}?
- ????}?
?
Authenticate事件是一个静态的变量,这样我们可以在程序的外部来订阅和取消订阅事件(非静态的public 事件在外部也是不能进行订阅和取消订阅事件的,这也是事件和委托的一个区别之一)
下面是我们的事件参数以及委托
- public?delegate?void?WebServiceAuthenticationEventHandler(Object?sender,?WebServiceAuthenticationEvent?e);?
- ?
- ???
- ???
- ???
- ??public?class?WebServiceAuthenticationEvent?:?EventArgs?
- ??{?
- ??????private?IPrincipal?_IPrincipalUser;?
- ??????private?HttpContext?_Context;?
- ??????private?string?_User;?
- ??????private?string?_Password;?
- ?
- ??????public?WebServiceAuthenticationEvent(HttpContext?context)?
- ??????{?
- ??????????_Context?=?context;?
- ??????}?
- ?
- ??????public?WebServiceAuthenticationEvent(HttpContext?context,?
- ??????????????????????string?user,?string?password)?
- ??????{?
- ??????????_Context?=?context;?
- ??????????_User?=?user;?
- ??????????_Password?=?password;?
- ??????}?
- ??????public?HttpContext?Context?
- ??????{?
- ??????????get?{?return?_Context;?}?
- ??????}?
- ??????public?IPrincipal?Principal?
- ??????{?
- ??????????get?{?return?_IPrincipalUser;?}?
- ??????????set?{?_IPrincipalUser?=?value;?}?
- ??????}?
- ??????public?void?Authenticate()?
- ??????{?
- ??????????GenericIdentity?i?=?new?GenericIdentity(User);?
- ??????????this.Principal?=?new?GenericPrincipal(i,?new?String[0]);?
- ??????}?
- ??????public?void?Authenticate(string[]?roles)?
- ??????{?
- ??????????GenericIdentity?i?=?new?GenericIdentity(User);?
- ??????????this.Principal?=?new?GenericPrincipal(i,?roles);?
- ??????}?
- ??????public?string?User?
- ??????{?
- ??????????get?{?return?_User;?}?
- ??????????set?{?_User?=?value;?}?
- ??????}?
- ??????public?string?Password?
- ??????{?
- ??????????get?{?return?_Password;?}?
- ??????????set?{?_Password?=?value;?}?
- ??????}?
- ??????public?bool?HasCredentials?
- ??????{?
- ??????????get?
- ??????????{?
- ??????????????if?((_User?==?null)?||?(_Password?==?null))?
- ??????????????????return?false;?
- ??????????????return?true;?
- ??????????}?
- ??????}?
- ??}?
?
我们在Global.asax的Application_Start方法里面把前面介绍的静态方法ServiceCredential.CheckUser订阅到我们Authenticate事件上,前面提到的增加和删除多种认证方式就是通过这种方法实现的.
- protected?void?Application_Start(object?sender,?EventArgs?e)?
- ???????{?
- ???????????WebServiceAuthenticationModule.Authenticate?+=?ServiceCredential.CheckUser;?
- ???????}?
?
我们在ServiceCredential.ValideUser方法设置了返回false,这是针对测试的一个配置,实际情况下我们可以和数据库结合起来写一个认证 运行上面讲解SoapHeader的那段代码,你会发现我们的认证已经有效了.关于文章中用到的Result类改天在用一篇文章记录一下,这是一个非常好的记录错误的方案.