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

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

  • 相关阅读:
    SAP 多料号展BOM
    SAP QM 检验批可用库存回转为待检验库存
    SAP QM UD检验批回转为REL待检验状态
    在ABAP中获取应用程序服务器的IP地址
    SAP连接外部数据库后批量写入数据
    Java调用Axis2用SAP WSDL生成的Stub文件
    用最新的采购信息记录更新采购单的价格——BAPI_PO_CHANGE
    SAP QM——QA01、QA02、QA03屏幕增强
    Java递归实现一、二、三级菜单查询
    ABAP——查询
  • 原文地址:https://www.cnblogs.com/Creator/p/1992019.html
Copyright © 2020-2023  润新知