在调用
Web Serivices
时
,
往往需要身份验证
,
使得通过验证的用户才能调用你
Web Serivices
中的方法
.
当然你可以通过将参数添加到每个需要自定义身份验证方案的
Web services
方法中去
,
这需要花费很大的精力
.IssueVision
中使用了非常常用而且有效便捷的方法
-----
使用
SoapHeader
来实现自定义身份验证数据的传递
.
SoapHeader 提供了一种方法,用于将数据传递到 Web services 方法或从 Web services 方法传递数据,条件是该数据不直接与 Web services 方法的主功能相关 . 你不用将参数添加到每个需要自定义身份验证方案的 Web services 方法,而可以将引用从 SoapHeader 派生的类的 SoapHeaderAttribute 应用于每个 Web services 方法。从 SoapHeader 派生的类的实现处理该自定义身份验证方案 . IssueVision 就是利用 SoapHeader 的这种能力来实现自定义身份验证数据传递的 .
我们来看一下如何利用 SoapHeader 来传递数据 .
SoapHeader 提供了一种方法,用于将数据传递到 Web services 方法或从 Web services 方法传递数据,条件是该数据不直接与 Web services 方法的主功能相关 . 你不用将参数添加到每个需要自定义身份验证方案的 Web services 方法,而可以将引用从 SoapHeader 派生的类的 SoapHeaderAttribute 应用于每个 Web services 方法。从 SoapHeader 派生的类的实现处理该自定义身份验证方案 . IssueVision 就是利用 SoapHeader 的这种能力来实现自定义身份验证数据传递的 .
我们来看一下如何利用 SoapHeader 来传递数据 .
1
.
首先需要在服务中定义一个从
SOAPHeader
派生的类
,
表示传入
SOAP
标头的数据
.
IssueVision 在中 IssueVisionWeb 项目 ( 此项目用于发布 Web Services) 中通过创建 CredentialSoapHeader 类来实现第一步 .
CredentialSoapHeader.cs
IssueVision 在中 IssueVisionWeb 项目 ( 此项目用于发布 Web Services) 中通过创建 CredentialSoapHeader 类来实现第一步 .
CredentialSoapHeader.cs
using System.Web.Services.Protocols;
namespace IssueVision.Web
{ public class CredentialSoapHeader : SoapHeader { private string m_username; private string m_password;
public string Username
{ get{ return m_username;}
set{ m_username = value;}
}
public string Password
{ get{ return m_password;}
set{ m_password = value;} } } } |
2 . 将服务的公共字段声明为该类型 , 使该 SoapHeader 在 Web Services 的公共合同中公开 , 并在创建代理时可由客户端使用 .
IssueVision 的 Web Services----IssueVisionServices.asmx 如此实现 .
IssueVisionServices.asmx 代码片断 :
public class IssueVisionServices : WebService
{
...
private CredentialSoapHeader m_credentials;
// custom SOAP header to pass credentials
public CredentialSoapHeader Credentials
{
get { return m_credentials; }
set { m_credentials = value; }
}
.......
}
3 . 在 Web Services 使用 SoapHeader 自定义属性定义一组关联的标头 , 服务中的每个 WebMethod 都可以使用 .( 默认情况下,标头是必需的,但也可以定义可选标头 )
IssueVisionServices.asmx 代码片断 :
....
[WebMethod(Description="Returns the lookup tables for IssueVision.")]
[SoapHeader("Credentials")]
public IVDataSet GetLookupTables()
{
SecurityHelper. VerifyCredentials (this);
return new IVData().GetLookupTables();
}
SecurityHelper 类的 VerifyCredentials 方法用来从 Web Services 中的 SoapHeader 类来得到自定义身份验证凭据 ( 如用户名和密码 ).
SecurityHelper.cs 代码片断如下 :
// verifies the clients credentials
public static void VerifyCredentials(IssueVisionServices service )
{
if (service.Credentials == null || service.Credentials.Username == null || service.Credentials.Password == null ) // 如果没有认证信息 , 返回 SoapException, 这样就不能匿名调用 Web Method 了
{
EventLogHelper.LogFailureAudit("A login was attempted with missing credential information.");
throw new SoapException(string.Empty, SoapException.ClientFaultCode, "Security");
}
public CredentialSoapHeader Credentials
{
get { return m_credentials; }
set { m_credentials = value; }
}
.......
}
3 . 在 Web Services 使用 SoapHeader 自定义属性定义一组关联的标头 , 服务中的每个 WebMethod 都可以使用 .( 默认情况下,标头是必需的,但也可以定义可选标头 )
IssueVisionServices.asmx 代码片断 :
....
[WebMethod(Description="Returns the lookup tables for IssueVision.")]
[SoapHeader("Credentials")]
public IVDataSet GetLookupTables()
{
SecurityHelper. VerifyCredentials (this);
return new IVData().GetLookupTables();
}
SecurityHelper 类的 VerifyCredentials 方法用来从 Web Services 中的 SoapHeader 类来得到自定义身份验证凭据 ( 如用户名和密码 ).
SecurityHelper.cs 代码片断如下 :
// verifies the clients credentials
public static void VerifyCredentials(IssueVisionServices service )
{
if (service.Credentials == null || service.Credentials.Username == null || service.Credentials.Password == null ) // 如果没有认证信息 , 返回 SoapException, 这样就不能匿名调用 Web Method 了
{
EventLogHelper.LogFailureAudit("A login was attempted with missing credential information.");
throw new SoapException(string.Empty, SoapException.ClientFaultCode, "Security");
}
string password = Authenticate(
service
.Credentials)
;
}
}
// authenticates a user's credentials passed in a custom SOAP header
private static string Authenticate( CredentialSoapHeader header )
{
DataSet dataSet = new DataSet();
string dbPasswordHash;
private static string Authenticate( CredentialSoapHeader header )
{
DataSet dataSet = new DataSet();
string dbPasswordHash;
try
{
SqlConnection conn = new SqlConnection(Common.ConnectionString);
SqlCommand cmd = new SqlCommand("GetUser", conn);
cmd.Parameters.Add("@UserName", header .Username );
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(dataSet);
}
catch (Exception ex)
{
EventLogHelper.LogFailureAudit(string.Format("The GetUser stored procedure encounted a problem: {0}", ex.ToString()));
throw new SoapException(string.Empty, SoapException.ServerFaultCode, "Database");
}
// does the user exist?
if (dataSet.Tables[0].Rows.Count == 0)
{
EventLogHelper.LogFailureAudit(string.Format("The username {0} does not exist.", header.Username));
throw new SoapException(string.Empty, SoapException.ClientFaultCode, "Security");
}
else
{
// we found the user, verify the password hash by compare the Salt + PasswordHash
DataRow dataRow = dataSet.Tables[0].Rows[0];
dbPasswordHash = (string)dataRow["PasswordHash"];
string dbPasswordSalt = (string)dataRow["PasswordSalt"];
{
SqlConnection conn = new SqlConnection(Common.ConnectionString);
SqlCommand cmd = new SqlCommand("GetUser", conn);
cmd.Parameters.Add("@UserName", header .Username );
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(dataSet);
}
catch (Exception ex)
{
EventLogHelper.LogFailureAudit(string.Format("The GetUser stored procedure encounted a problem: {0}", ex.ToString()));
throw new SoapException(string.Empty, SoapException.ServerFaultCode, "Database");
}
// does the user exist?
if (dataSet.Tables[0].Rows.Count == 0)
{
EventLogHelper.LogFailureAudit(string.Format("The username {0} does not exist.", header.Username));
throw new SoapException(string.Empty, SoapException.ClientFaultCode, "Security");
}
else
{
// we found the user, verify the password hash by compare the Salt + PasswordHash
DataRow dataRow = dataSet.Tables[0].Rows[0];
dbPasswordHash = (string)dataRow["PasswordHash"];
string dbPasswordSalt = (string)dataRow["PasswordSalt"];
// create a hash based on the user's salt and the input password
string passwordHash = HashString(dbPasswordSalt + header.Password);
// does the computed hash match the database hash?string passwordHash = HashString(dbPasswordSalt + header.Password);
if (string.Compare(dbPasswordHash, passwordHash) != 0)
{
EventLogHelper.LogFailureAudit(string.Format("The password for the username {0} was incorrect.", header.Username));
throw new SoapException(string.Empty, SoapException.ClientFaultCode, "Security");
}
}
return dbPasswordHash;
}
4 . 最后客户端在调用要求标头的方法之前,需直接在代理类上设置标头 .
IssueVision 的 SmartClient 端的 WebServicesLayer 类来调用此 Web Services
WebServicesLayer.cs 程序片断如下 :
private static IssueVisionServices GetWebServiceReference(string username, string password)
{
IssueVisionServices dataService = new IssueVisionServices();
//<ReplaceWithWse>
CredentialSoapHeader header = new CredentialSoapHeader();
header.Username = username;
header.Password = password;
dataService.CredentialSoapHeaderValue = header;
//</ReplaceWithWse>
InitWebServiceProxy(dataService);
return dataService;
}
通过以上步骤就可以完成 Web Services 自定义身份验证了 .IssueVision 中还有很多相关的操作 , 因为在这里只是讨论一下 SoapHeader 的用法