webservice安全性之 SoapHeader自定義身份驗證


相信很多開發者都用過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的代碼,多余的方法將會在后面用到

客戶端:

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寫一個處理模塊,那么它的原理是什么呢?具體進行了哪些操作呢?我們的思路如下:

  1. HTTP Module 分析 HTTP 消息以檢查它們是不是 SOAP 消息。
  2. 如果 HTTP Module 檢測到 SOAP 消息,它會讀取 SOAP 標頭。
  3. 如果 SOAP 消息的 SOAP 標頭中有身份驗證憑據,HTTP Module 將引發一個自定義 global.asax 事件。
 

下面來看看我們的Module代碼

  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 事件在外部也是不能進行訂閱和取消訂閱事件的,這也是事件和委托的一個區別之一)

下面是我們的事件參數以及委托

 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的那段代碼,你會發現我們的認證已經有效了.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM