相信很多開發者都用過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的代碼,多餘的方法將會在後面用到
用戶端當我們擁有很多個類的時候,要添加一個或者刪除一個驗證方式(假設需要進行多種認證)是非常麻煩的,我們不可能跑到每個方法裡面去加一個方法調用,這是災難性的工作,當然我們也可以用AOP來實現,Aop的話需要額外增加很多代碼或者直接引入協力廠商來做,但是我們可不可以有更簡便的方法呢?
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("驗證錯誤", "不能確認您的身份,請檢查使用者名和密碼");
}
}
}

 

OK,答案就是使用HttpModule,我們集成IHttpModule寫一個處理模組,那麼它的原理是什麼呢?具體進行了哪些操作呢?我們的思路如下:
HTTP Module 分析 HTTP 消息以檢查它們是不是 SOAP 訊息。
如果 HTTP Module 檢測到 SOAP 訊息,它會讀取 SOAP 標頭。
如果 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類改天在用一篇文章記錄一下,這是一個非常好的記錄錯誤的方案.
arrow
arrow
    全站熱搜

    戮克 發表在 痞客邦 留言(0) 人氣()