如何:执行使用 SOAP 标头的自定义身份验证

下面的自定义解决方案是使用 ASP.NET 生成的,目的是为了提供一种使用 SOAP 标头的身份验证机制。 该解决方案涉及到 Web 服务器上的一个自定义 IHttpModule,此模块执行下列步骤:

  1. HTTP Module 分析 HTTP 消息以检查它们是不是 SOAP 消息。

  2. 如果 HTTP Module 检测到 SOAP 消息,它会读取 SOAP 标头。

  3. 如果 SOAP 消息的 SOAP 标头中有身份验证凭据,HTTP Module 将引发一个自定义 global.asax 事件。

在提供的示例中,HTTP Module 对用户进行身份验证并设置 Context 属性,Web 服务可以使用这些属性确定客户端是否有权访问该 Web 服务。

Note注意:

在此示例中,文本通过网络以明文形式发送(未加密)。 如果明文对您的应用程序不够安全,请添加一种加密算法。

示例

下面的代码示例是一个 HTTP 模块,它分析 HTTP 消息中是否有 SOAP 请求。 如果该 HTTP 消息是 SOAP 消息,则将引发自定义 WebServiceAuthenticationEvent

using System;
using System.Web;
using System.IO;
using System.Xml;
using System.Xml.XPath;
using System.Text;
using System.Web.Services.Protocols;

namespace Microsoft.WebServices.Security {

      public sealed class WebServiceAuthenticationModule : IHttpModule 
      {
         private WebServiceAuthenticationEventHandler 
                       _eventHandler = null;

         public event WebServiceAuthenticationEventHandler Authenticate 
         {
           add { _eventHandler += value;}
           remove {_eventHandler -= value;}
         }
 
         public void Dispose() 
         {
         }

         public void Init(HttpApplication app) 
         {
           app.AuthenticateRequest += new
                      EventHandler(this.OnEnter);
         }

         private void OnAuthenticate(WebServiceAuthenticationEvent e) 
         {
           if (_eventHandler == null)
               return;

             _eventHandler(this, e);
             if (e.User != null)
                e.Context.User = e.Principal;
         }

         public string ModuleName
         {
           get{ return "WebServiceAuthentication"; }
         }

         void OnEnter(Object source, EventArgs eventArgs) {
           HttpApplication app = (HttpApplication)source;
           HttpContext context = app.Context;
           Stream HttpStream = context.Request.InputStream;

           // Save the current position of stream.
           long posStream = HttpStream.Position;

           // If the request contains an HTTP_SOAPACTION 
           // header, look at this message.
           if (context.Request.ServerVariables["HTTP_SOAPACTION"]
                          == null)
              return;

           // Load the body of the HTTP message
           // into an XML document.
           XmlDocument dom = new XmlDocument();
           string soapUser;
           string soapPassword;

           try 
           {
             dom.Load(HttpStream);

             // Reset the stream position.
             HttpStream.Position = posStream;

             // Bind to the Authentication header.
             soapUser =
                 dom.GetElementsByTagName("User").Item(0).InnerText;
             soapPassword =
                 dom.GetElementsByTagName("Password").Item(0).InnerText;
           } 
           catch (Exception e) 
           {
             // Reset the position of stream.
             HttpStream.Position = posStream;

             // Throw a SOAP exception.
             XmlQualifiedName name = new
                          XmlQualifiedName("Load");
             SoapException soapException = new SoapException(
                       "Unable to read SOAP request", name, e);
             throw soapException;
           }

           // Raise the custom global.asax event.
           OnAuthenticate(new WebServiceAuthenticationEvent                        (context, soapUser, soapPassword));
           return;
      }
    }
}
Public NotInheritable Class WebServiceAuthenticationModule
        Implements IHttpModule

        Public Delegate Sub WebServiceAuthenticationEventHandler(ByVal sender As [Object], ByVal e As WebServiceAuthenticationEvent)
        Private _eventHandler As WebServiceAuthenticationEventHandler = Nothing

        Public Custom Event Authenticate As 
                      WebServiceAuthenticationEventHandler
            AddHandler(ByVal value As 
                           WebServiceAuthenticationEventHandler)
                _eventHandler = value
            End AddHandler
            RemoveHandler(ByVal value As 
                       WebServiceAuthenticationEventHandler)
                _eventHandler = value
            End RemoveHandler
            RaiseEvent(ByVal sender As Object, 
                    ByVal e As WebServiceAuthenticationEvent)
            End RaiseEvent
        End Event

        Public Sub Dispose() Implements System.Web.IHttpModule.Dispose

        End Sub 'Dispose


        Public Sub Init(ByVal app As HttpApplication) Implements System.Web.IHttpModule.Init
            AddHandler app.AuthenticateRequest, AddressOf Me.OnEnter

        End Sub 'Init


        Private Sub OnAuthenticate(ByVal e As WebServiceAuthenticationEvent)
            If _eventHandler Is Nothing Then
                Return
            End If
            _eventHandler(Me, e)
            If Not (e.User Is Nothing) Then
                e.Context.User = e.Principal
            End If

        End Sub 'OnAuthenticate 

        Public ReadOnly Property ModuleName() As String
            Get
                Return "WebServiceAuthentication"
            End Get
        End Property

        Sub OnEnter(ByVal [source] As [Object], ByVal eventArgs As EventArgs)
            Dim app As HttpApplication = CType([source], HttpApplication)
            Dim context As HttpContext = app.Context
            Dim HttpStream As Stream = context.Request.InputStream

            ' Save the current position of stream.
            Dim posStream As Long = HttpStream.Position

            ' If the request contains an HTTP_SOAPACTION 
            ' header, look at this message.
            If context.Request.ServerVariables("HTTP_SOAPACTION") Is Nothing Then
                Return
            End If
            ' Load the body of the HTTP message
            ' into an XML document.
            Dim dom As New XmlDocument()
            Dim soapUser As String
            Dim soapPassword As String

            Try
                dom.Load(HttpStream)

                ' Reset the stream position.
                HttpStream.Position = posStream

                ' Bind to the Authentication header.
                soapUser = dom.GetElementsByTagName("User").Item(0).InnerText
                soapPassword = dom.GetElementsByTagName("Password").Item(0).InnerText
            Catch e As Exception
                ' Reset the position of stream.
                HttpStream.Position = posStream

                ' Throw a SOAP exception.
                Dim name As New XmlQualifiedName("Load")
                Dim soapException As New SoapException("Unable to read SOAP request", name, e)
                Throw soapException
            End Try

            ' Raise the custom global.asax event.
            OnAuthenticate(New WebServiceAuthenticationEvent(context, soapUser, soapPassword))
            Return

        End Sub 'OnEnter
    End Class 'WebServiceAuthenticationModule

下面的代码示例是一个自定义身份验证事件,如果收到 SOAP 请求,则由 HTTP Module 引发该事件。



namespace Microsoft.WebServices.Security {
    using System;
    using System.Web;
    using System.Security.Principal;

    public class WebServiceAuthenticationEvent : EventArgs {
       private Iprincipal _IPrincipalUser;
       private HttpContext _Context;
       private string _User;
       private string _Password;

       public WebServiceAuthenticationEvent(HttpContext context)
       {
            _Context = context;
       }

       public WebServiceAuthenticationEvent(HttpContext context,
                       string user, string password)
       {
           _Context = context;
           _User = user;
           _Password = password;
       }
       public  HttpContext Context 
       { 
         get { return _Context;}
       }
       public IPrincipal Principal 
       { 
         get { return _IPrincipalUser;} 
         set { _IPrincipalUser = value;}
       }
       public void Authenticate()
       {
         GenericIdentity i = new GenericIdentity(User);
         this.Principal = new GenericPrincipal(i, new String[0]);
       }
       public void Authenticate(string[] roles)
       {
         GenericIdentity i = new GenericIdentity(User);
         this.Principal = new GenericPrincipal(i, roles);
       }
       public string User 
       {
         get { return _User; }
         set { _User = value; }
       }
       public string Password
       {
         get { return _Password; }
         set { _Password = value; }
       }
       public bool HasCredentials {
         get 
         {
              if ((_User == null) || (_Password == null))
                return false;
              return true;
         }
       }
    }
}
Imports System
Imports System.Web
Imports System.Security.Principal

Public Class WebServiceAuthenticationEvent
    Inherits EventArgs
    Private _IPrincipalUser As Iprincipal
    Private _Context As HttpContext
    Private _User As String
    Private _Password As String
    
    
    Public Sub New(ByVal context As HttpContext) 
        _Context = context
    
    End Sub 'New
    
    
    Public Sub New(ByVal context As HttpContext, ByVal user As String, ByVal password As String) 
        _Context = context
        _User = user
        _Password = password
    
    End Sub 'New
    
    Public ReadOnly Property Context() As HttpContext 
        Get
            Return _Context
        End Get
    End Property
    
    Public Property Principal() As IPrincipal 
        Get
            Return _IPrincipalUser
        End Get
        Set
            _IPrincipalUser = value
        End Set
    End Property
    
    Overloads Public Sub Authenticate() 
        Dim i As New GenericIdentity(User)
        Me.Principal = New GenericPrincipal(i, New String(-1) {})
    
    End Sub 'Authenticate
    
    Overloads Public Sub Authenticate(ByVal roles() As String) 
        Dim i As New GenericIdentity(User)
        Me.Principal = New GenericPrincipal(i, roles)
    
    End Sub 'Authenticate
    
    Public Property User() As String 
        Get
            Return _User
        End Get
        Set
            _User = value
        End Set
    End Property
    
    Public Property Password() As String 
        Get
            Return _Password
        End Get
        Set
            _Password = value
        End Set
    End Property
    
    Public ReadOnly Property HasCredentials() As Boolean 
        Get
            If _User Is Nothing OrElse _Password Is Nothing Then
                Return False
            End If
            Return True
        End Get
    End Property
End Class 'WebServiceAuthenticationEvent

下面的代码示例是自定义 WebServiceAuthenticationEvent 事件的委托。

namespace Microsoft.WebServices.Security 
{
   using System;

   public delegate void WebServiceAuthenticationEventHandler(Object sender,  WebServiceAuthenticationEvent e);
}
Imports System

Public Delegate Sub WebServiceAuthenticationEventHandler(ByVal sender As [Object], ByVal e As WebServiceAuthenticationEvent)

下面的代码示例是一个 Web 服务,该 Web 服务定义客户端必须传递的 Authentication SOAP 标头。 该 Web 服务不必执行身份验证, 而是可以检查 User.Identity.IsAuthenticated 属性以确定 HTTP Module 是否已对用户进行身份验证。

<%@ WebService Language="C#" Class="SecureWebService" %>

using System;
using System.Web.Services;
using System.Web.Services.Protocols;

public class Authentication : SoapHeader {
  public string User;
  public string Password;
}

public class SecureWebService : WebService{
  public Authentication authentication;

  [WebMethod]
  [SoapHeader("authentication")]
  public string ValidUser(){
    if (User.IsInRole("Customer"))
      return "User is in role customer";

    if (User.Identity.IsAuthenticated)
      return "User is a valid user";
    return "not authenticated";
  }
}
<%@ WebService Language="VB" Class="SecureWebService" %>

Imports System 
Imports System.Web.Services
Imports System.Web.Services.Protocols

Public Class Authentication
    Inherits SoapHeader
    Public User As String
    Public Password As String
End Class 'Authentication

Public Class SecureWebService
    Inherits WebService
    Public authentication As Authentication
    
    
    <WebMethod(), SoapHeader("authentication")>  _
    Public Function ValidUser() As String 
        If User.IsInRole("Customer") Then
            Return "User is in role customer"
        End If 
        If User.Identity.IsAuthenticated Then
            Return "User is a valid user"
        End If
        Return "not authenticated"
    
    End Function 'ValidUser
End Class 'SecureWebService

下面的代码示例是一个 Web 服务客户端,它在 Authentication SOAP 标头中传递自定义 SOAP 标头身份验证机制所必需的凭据。

    // Create a new instance of a Web service proxy class.
    SecureWebService s = new SecureWebService();

    // Create the Authentication SOAP header and set values.
    Authentication a = new Authentication();
    a.User = user.Value;
    a.Password = password.Value;

    // Assign the Header.
    s.AuthenticationValue = a;

      string result = s.ValidUser();
      span1.InnerHtml = result;
     ' Create a new instance of a Web service proxy class.
     Dim s As New SecureWebService()

     ' Create the Authentication SOAP header and set values.
     Dim a As New Authentication()
     a.User = user.Value
     a.Password = password.Value

     Assign the Header.
     s.AuthenticationValue = a

     Dim result As String = s.ValidUser()
     span1.InnerHtml = result

请参见

任务

如何:将 XML Web services 配置为使用 Windows 身份验证

参考

NetworkCredential
CredentialCache
X509Certificate

概念

保证使用 ASP.NET 创建的 XML Web services 的安全

其他资源

Securing ASP.NET Web Applications
使用 ASP.NET 的 XML Web services

Footer image

版权所有 (C) 2007 Microsoft Corporation。保留所有权利。