微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

相信很多开发者都用过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方法设置了返回false,这是针对测试的一个配置,实际情况下我们可以和数据库结合起来写一个认证
运行上面讲解SoapHeader的那段代码,你会发现我们的认证已经有效了.关于文章中用到的Result类改天在用一篇文章记录一下,这是一个非常好的记录错误的方案.

很晚了,洗洗睡了,各位有什么好的想法可以留言,本人技术有限,权当抛砖引玉!!!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐