加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

WebService的安全性讨论【身份识别】

发布时间:2020-12-17 01:09:28 所属栏目:安全 来源:网络整理
导读:相信很多开发者都用过WebService来实现程序的面向服务,本文主要介绍WebService的身份识别实现方式,当然本文会提供一个不是很完善的例子,权当抱砖引玉了. 首先我们来介绍webservice下的两种验证方式, 一.通过集成windows身份验证 通过集成windows方式解决webs

相信很多开发者都用过WebService来实现程序的面向服务,本文主要介绍WebService的身份识别实现方式,当然本文会提供一个不是很完善的例子,权当抱砖引玉了.

首先我们来介绍webservice下的两种验证方式,

一.通过集成windows身份验证

通过集成windows方式解决webservice的安全问题是一个很简洁,并且行之有效的解决方案,该方案的优点是比较安全,性能较好,当然因为与windows紧密的结合到了一起,缺点自然也很明显了,第一,不便于移植,第二,要进行相关的配置部署工作(当然我们也可以用代码来操作IIS,只不过比较麻烦,最近一直做自动化部署,所以一讲到配置马上就会联想到怎么去自动部署)

具体怎么做呢?

服务器端:配置IIS虚拟目录为集成windows身份验证

客户端:

Service1 wr = new Service1(); //web service实例??

wr.Credentials = new NetworkCredential("administrator","123"); //用户名密码??

lblTest.Text = wr.Add(2,2).ToString(); //调用Add的 web service方法?

二.使用 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的代码,多余的方法将会在后面用到

客户端

View Code
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();????????????
????????}
????}

服务器端

View Code
?? 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写一个处理模块,那么它的原理是什么呢?具体进行了哪些操作呢?我们的思路如下:

  1. HTTP Module 分析 HTTP 消息以检查它们是不是 SOAP 消息。
  2. 如果 HTTP Module 检测到 SOAP 消息,它会读取 SOAP 标头。
  3. 如果 SOAP 消息的 SOAP 标头中有身份验证凭据,HTTP Module 将引发一个自定义 global.asax 事件。

下面来看看我们的Module代码

?

View Code
??1? ? public ? class ?WebServiceAuthenticationModule?:?IHttpModule
??2? ????{
??3? ???????? private ? static ?WebServiceAuthenticationEventHandler
??4? ??????????????????????_eventHandler? = ? null ;
??5? ???????? /// ? <summary>
??6? ???????? /// ?验证事件.绑定到此事件可进行对用户身份的识别
??7? ???????? /// ? </summary>
??8? ???????? public ? static ? event ?WebServiceAuthenticationEventHandler?Authenticate
??9? ????????{
?10? ????????????add?{?_eventHandler? += ?value;?}
?11? ????????????remove?{?_eventHandler? -= ?value;?}
?12? ????????}
?13? ???????? public ?Result?Result? = ? new ?Result();
?14?
?15? ???????? public ? void ?Dispose()
?16? ????????{
?17? ????????}
?18? ???????? public ? void ?Init(HttpApplication?app)
?19? ????????{
?20? ????????????app.AuthenticateRequest? += ? new
?21? ???????????????????????EventHandler( this .OnEnter);
?22? ????????????Result.EndValid? += ? new ?
?23? ????????????????EventHandler( this .OnCheckError);
?24? ????????}
?25?
?26? ???????? /// ? <summary>
?27? ???????? /// ?验证用户身份
?28? ???????? /// ? </summary>
?29? ???????? /// ? <param?name="e"></param>
?30? ???????? private ? void ?OnAuthenticate(WebServiceAuthenticationEvent?e)
?31? ????????{
?32? ???????????? if ?(_eventHandler? == ? null )
?33? ???????????????? return ;
?34?
?35? ????????????_eventHandler( this ,?e);
?36? ???????????? if ?(e.User? != ? null )
?37? ????????????????e.Context.User? = ?e.Principal;
?38? ????????}
?39?
?40? ???????? public ? string ?ModuleName
?41? ????????{
?42? ???????????? get ?{? return ? " WebServiceAuthentication " ;?}
?43? ????????}
?44?
?45? ???????? void ?OnEnter(Object?source,?EventArgs?eventArgs)
?46? ????????{
?47? ????????????HttpApplication?app? = ?(HttpApplication)source;
?48? ????????????HttpContext?context? = ?app.Context;
?49? ????????????Stream?HttpStream? = ?context.Request.InputStream;
?50?
?51? ???????????? // ?Save?the?current?position?of?stream.
?52? ???????????? long ?posStream? = ?HttpStream.Position;
?53?
?54? ???????????? // ?If?the?request?contains?an?HTTP_SOAPACTION?
?55? ???????????? // ?header,?look?at?this?message.HTTP_SOAPACTION
?56? ???????????? if ?(context.Request.ServerVariables[ " HTTP_SOAPACTION " ]? == ? null )
?57? ???????????????? return ;
?58?
?59? ???????????? // ?Load?the?body?of?the?HTTP?message
?60? ???????????? // ?into?an?XML?document.
?61? ????????????XmlDocument?dom? = ? new ?XmlDocument();
?62? ???????????? string ?soapUser;
?63? ???????????? string ?soapPassword;
?64?
?65? ???????????? try
?66? ????????????{
?67? ????????????????dom.Load(HttpStream);
?68?
?69? ???????????????? // ?Reset?the?stream?position.
?70? ????????????????HttpStream.Position? = ?posStream;
?71?
?72? ???????????????? // ?Bind?to?the?Authentication?header.
?73? ????????????????soapUser? =
?74? ????????????????????dom.GetElementsByTagName( " User " ).Item( 0 ).InnerText;
?75? ????????????????soapPassword? =
?76? ????????????????????dom.GetElementsByTagName( " Password " ).Item( 0 ).InnerText;
?77? ????????????}
?78? ???????????? catch ?(Exception?e)
?79? ????????????{
?80? ???????????????? // ?Reset?the?position?of?stream.
?81? ????????????????HttpStream.Position? = ?posStream;
?82?
?83? ???????????????? // ?Throw?a?SOAP?exception.
?84? ????????????????XmlQualifiedName?name? = ? new
?85? ?????????????????????????????XmlQualifiedName( " Load " );
?86? ????????????????SoapException?soapException? = ? new ?SoapException(
?87? ?????????????????????????? " SOAP请求没有包含必须的身份识别信息 " ,?name,?e);
?88? ???????????????? throw ?soapException;
?89? ????????????}
?90? ???????????? // ?触发全局事件
?91? ????????????OnAuthenticate( new ?WebServiceAuthenticationEvent
?92? ?????????????????????????(context,?soapUser,?soapPassword));
?93? ????????????Result.OnEndValid();
?94? ???????????? return ;
?95? ????????}
?96? ???????? void ?OnCheckError(Object?sender,?EventArgs?e)
?97? ????????{
?98? ???????????? if ?(Result.BrokenRules.Count? == ? 0 )
?99? ????????????{
100? ???????????????? return ;
101? ????????????}
102? ???????????? else
103? ????????????{
104? ????????????????HttpApplication?app? = ?HttpContext.Current.ApplicationInstance;
105? ????????????????app.CompleteRequest();
106? ????????????????app.Context.Response.Write(Result.Error);
107? ????????????}
108? ????????}
109? ????}

?

Authenticate事件是一个静态的变量,这样我们可以在程序的外部来订阅和取消订阅事件(非静态的public 事件在外部也是不能进行订阅和取消订阅事件的,这也是事件和委托的一个区别之一)

下面是我们的事件参数以及委托

?

View Code
?1? ?? public ? delegate ? void ?WebServiceAuthenticationEventHandler(Object?sender,?WebServiceAuthenticationEvent?e);
?2?
?3? ???? /// ? <summary>
?4? ???? /// ?封装的事件参数
?5? ???? /// ? </summary>
?6? ???? public ? class ?WebServiceAuthenticationEvent?:?EventArgs
?7? ????{
?8? ???????? private ?IPrincipal?_IPrincipalUser;
?9? ???????? private ?HttpContext?_Context;
10? ???????? private ? string ?_User;
11? ???????? private ? string ?_Password;
12?
13? ???????? public ?WebServiceAuthenticationEvent(HttpContext?context)
14? ????????{
15? ????????????_Context? = ?context;
16? ????????}
17?
18? ???????? public ?WebServiceAuthenticationEvent(HttpContext?context,
19? ???????????????????????? string ?user,? string ?password)
20? ????????{
21? ????????????_Context? = ?context;
22? ????????????_User? = ?user;
23? ????????????_Password? = ?password;
24? ????????}
25? ???????? public ?HttpContext?Context
26? ????????{
27? ???????????? get ?{? return ?_Context;?}
28? ????????}
29? ???????? public ?IPrincipal?Principal
30? ????????{
31? ???????????? get ?{? return ?_IPrincipalUser;?}
32? ???????????? set ?{?_IPrincipalUser? = ?value;?}
33? ????????}
34? ???????? public ? void ?Authenticate()
35? ????????{
36? ????????????GenericIdentity?i? = ? new ?GenericIdentity(User);
37? ???????????? this .Principal? = ? new ?GenericPrincipal(i,? new ?String[ 0 ]);
38? ????????}
39? ???????? public ? void ?Authenticate( string []?roles)
40? ????????{
41? ????????????GenericIdentity?i? = ? new ?GenericIdentity(User);
42? ???????????? this .Principal? = ? new ?GenericPrincipal(i,?roles);
43? ????????}
44? ???????? public ? string ?User
45? ????????{
46? ???????????? get ?{? return ?_User;?}
47? ???????????? set ?{?_User? = ?value;?}
48? ????????}
49? ???????? public ? string ?Password
50? ????????{
51? ???????????? get ?{? return ?_Password;?}
52? ???????????? set ?{?_Password? = ?value;?}
53? ????????}
54? ???????? public ? bool ?HasCredentials
55? ????????{
56? ???????????? get
57? ????????????{
58? ???????????????? if ?((_User? == ? null )? || ?(_Password? == ? null ))
59? ???????????????????? return ? false ;
60? ???????????????? return ? true ;
61? ????????????}
62? ????????}
63? ????}

?

我们在Global.asax的Application_Start方法里面把前面介绍的静态方法ServiceCredential.CheckUser订阅到我们Authenticate事件上,前面提到的增加和删除多种认证方式就是通过这种方法实现的.

? protected void Application_Start(object sender,EventArgs e)
?????? {
?????????? WebServiceAuthenticationModule.Authenticate += ServiceCredential.CheckUser;
?????? }

我们在ServiceCredential.ValideUser方法设置了返回true,这是针对测试的一个配置,实际情况下我们可以和数据库结合起来写一个认证
运行上面讲解SoapHeader的那段代码,你会发现我们的认证已经有效了.关于文章中用到的Result类改天在用一篇文章记录一下,这是一个非常好的记录错误的方案.
http://www.cnblogs.com/Creator/archive/2011/03/23/1992019.html

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读