Web Services Software Factory
Web服务软件工厂(WSSF)演练之三:创建服务契约和实现方法
关键字:Web Services Software Factory, Service Contracts, Service Implementation
您应该已经完成了本演练的第1部分和第2部分,然后再开始这一部分的演练。
今天的教程将为我们的WCF服务创建服务契约和实际实现方法。 首先,在您的解决方案资源管理器中,右键点击MyCryptographyService.model后Add–>New Model。
具体的命名请参见下图所示。
如果您的开发环境没有出现属性窗口,右击空白设计区域,然后单击属性。跟我们创建数据契约一样, Project Mapping Table选择我们对应的项目 “Implementation Technology”下拉列表中选择 WCF Extension。设置“Serializer Type”为DataContractSerializer 。
通过第二部分的练习,你应该熟悉如何操作我们的设计区域 ,工具箱的内容将显示与创建服务契约相关的工具。
拖动一个Service并命名为CryptService,一个Service Contract并命名为CryptService,一个Operation并命名为EncryptString,和两个messages分别命名为 EncryptStringRequest和EncryptStringResponse。到我们的空白设计区域,如图所示排列他们。
使用“Connector”连接工具连接Service Contract Service对象(也可以在Service的属性窗口中设置Service Contract的属性)。下面将是设定Operation和Service Contract及messages之间关系的操作,但这部分使用。“Connector”连接工具有些繁琐,可以直接定义Operation的Service Contract的属性为CryptService,“Request”属性为EncryptStringRequest,“Response”的属性为EncryptStringResponse即可。设置好以后应该如下图所示。
按照上述步骤,从工具箱中在拖动2个Operations到您的设计区域,并分别命名为DecryptString和HashString 。工具箱中在拖动4个Messages到设计区域,并分别命名为DecryptStringRequest 、DecryptStringResponse 、 HashStringRequest 和HashStringResponse 。Operations连接各对象间的连接关系参照EncryptString 的各项属性设置,完成后如下图所示 。
继续努力,我们已经接近成功。下面需要给messages对象添加一些Data Contract Message Part。右键单击EncryptStringRequest选择Add–>Data Contract Message Part。将其命名为EncryptionObject 。单击我们刚刚建立的EncryptionObject,在属性窗口中设置Type。单击(...)按钮,将弹出如下图所示的此对话框。选择逐层展开后选择EncryptionObject单击确定。
继续为DecryptStringRequest对象添加一个Data Contract Message Part 命名为EncryptionObject,为HashStringRequest添加命名为HashObject。并对应设置好Type属性。继续分别为3个Response messages对象添加一个Primitive Message Parts,分别命名为“ReturnValue”。需要注意的是,每个名称输入时要精确,名称字符串结尾不能留有空格,在您采用Copy / Paste名称时尤其要注意这一点,否则检查起来很麻烦。
现在,在设计区域的某个白色地方点击右键,选择Validate All(全部验证)如果返回一些错误,请逐一认真检查。如果全部验证已经通过,右键单击,并选择Generate Code(生成代码)。随后,将会有更多的生成代码出现在MyCryptographyService.ServiceImplementation和MyCryptographyService.ServiceContracts 解决方案的类库项目中。
下一步,右键点击MyCryptographyService.BusinessLogic项目,并选择添加一个Class(类)。命名为CryptographyServiceBusinessLogic.cs 。为了节省时间,我已经准备好了如下的代码,请直接使用如下的代码。
using MyCryptographyService.BusinessEntities;
using MyCryptographyService.DataAccess;
namespace MyCryptographyService.BusinessLogic
{
public class CryptographyServiceBusinessLogic
{
private CryptManager manager = new CryptManager();
public string EncryptString(EncryptionObject encryptionObject)
{
return manager.EncryptString(encryptionObject);
}
public string DecryptString(EncryptionObject encryptionObject)
{
return manager.DecryptString(encryptionObject);
}
public string HashString(HashObject hashObject)
{
return manager.HashString(hashObject);
}
}
}
using MyCryptographyService.BusinessEntities;
using MyCryptographyService.DataAccess;
namespace MyCryptographyService.BusinessLogic
{
///
/// This class isn't going to do much in this tutorial.
/// However, in here, you could apply business logic to your service,
/// and maybe make some decisions about which methods in the Data Access Layer's
/// "manager" that you wanted to call. For this example, it is just an
/// "enterprisey" pass-through to show the concept.
///
public class CryptographyServiceBusinessLogic
{
private CryptManager manager = new CryptManager();
public string EncryptString(EncryptionObject encryptionObject)
{
return manager.EncryptString(encryptionObject);
}
public string DecryptString(EncryptionObject encryptionObject)
{
return manager.DecryptString(encryptionObject);
}
public string HashString(HashObject hashObject)
{
return manager.HashString(hashObject);
}
}
}
Next, right click on the MyCryptographyService.DataAccess project and add a new class called CryptographyServiceManager. The code for it is below.
下一步,右键点击MyCryptographyService.DataAccess项目,并添加一个新类称为CryptographyServiceManager 。它的代码如下。
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using MyCryptographyService.BusinessEntities;
namespace MyCryptographyService.DataAccess
{
/// <SUMMARY>
///
/// </SUMMARY>
public class CryptManager
{
private const string KEY = "$0ftw@r3";
private const string IV = "@p3t30n&";
private DESCryptoServiceProvider des = new DESCryptoServiceProvider();
public CryptManager()
{
des.Key = ASCIIEncoding.ASCII.GetBytes(KEY);
des.IV = ASCIIEncoding.ASCII.GetBytes(IV);
}
public string HashString(HashObject hashObject)
{
switch (hashObject.HashType)
{
case HashType.MD5:
return getMd5Sum(hashObject.StringToHash);
case HashType.SHA256:
return getSha256Hash(hashObject.StringToHash);
default:
throw new NotImplementedException("That Hashing Algorithm is not supported");
}
}
public string EncryptString(EncryptionObject encryptionObject)
{
string cryptText = string.Empty;
switch (encryptionObject.EncryptionAlgorithm)
{
case EncryptionAlgorithm.DES:
cryptText = encryptDes(encryptionObject.Text);
break;
case EncryptionAlgorithm.Rijndael:
cryptText = encryptRijndael(encryptionObject.Text);
break;
default:
throw new NotImplementedException("You provided an algorithm that is not implemented.");
}
return cryptText;
}
public string DecryptString(EncryptionObject encryptionObject)
{
string plainText = string.Empty;
switch (encryptionObject.EncryptionAlgorithm)
{
case EncryptionAlgorithm.DES:
plainText = decryptDes(encryptionObject.Text);
break;
case EncryptionAlgorithm.Rijndael:
plainText = decryptRijndael(encryptionObject.Text);
break;
default:
throw new NotImplementedException("You provided an algorithm that is not implemented.");
}
return plainText;
}
private string encryptDes(string plainText)
{
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, des.CreateEncryptor(), CryptoStreamMode.Write);
StreamWriter writer = new StreamWriter(cryptoStream);
writer.Write(plainText);
writer.Flush();
cryptoStream.FlushFinalBlock();
writer.Flush();
return Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
private string decryptDes(string cryptText)
{
MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(cryptText));
CryptoStream cryptoStream = new CryptoStream(memoryStream, des.CreateDecryptor(), CryptoStreamMode.Read);
StreamReader reader = new StreamReader(cryptoStream);
return reader.ReadToEnd();
}
private string encryptRijndael(string plainText)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
byte[] plainBytes = Encoding.Unicode.GetBytes(plainText);
byte[] salt = Encoding.ASCII.GetBytes(KEY.Length.ToString());
//This class uses an extension of the PBKDF1 algorithm defined in the PKCS#5 v2.0
//standard to derive bytes suitable for use as key material from a password.
//The standard is documented in IETF RRC 2898.
PasswordDeriveBytes secretKey = new PasswordDeriveBytes(KEY, salt);
//Creates a symmetric encryptor object.
ICryptoTransform encryptor = rijndaelCipher.CreateEncryptor(secretKey.GetBytes(32), secretKey.GetBytes(16));
MemoryStream memoryStream = new MemoryStream();
//Defines a stream that links data streams to cryptographic transformations
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainBytes, 0, plainBytes.Length);
//Writes the final state and clears the buffer
cryptoStream.FlushFinalBlock();
byte[] cipherBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
string encryptedData = Convert.ToBase64String(cipherBytes);
return encryptedData;
}
private string decryptRijndael(string cipherText)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
byte[] encryptedData = Convert.FromBase64String(cipherText);
byte[] salt = Encoding.ASCII.GetBytes(KEY.Length.ToString());
//Making of the key for decryption
PasswordDeriveBytes secretKey = new PasswordDeriveBytes(KEY, salt);
//Creates a symmetric Rijndael decryptor object.
ICryptoTransform decryptor = rijndaelCipher.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16));
MemoryStream memoryStream = new MemoryStream(encryptedData);
//Defines the cryptographics stream for decryption.THe stream contains decrpted data
CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
byte[] plainText = new byte[encryptedData.Length];
int decryptedCount = cryptoStream.Read(plainText, 0, plainText.Length);
memoryStream.Close();
cryptoStream.Close();
//Converting to string
string decryptedData = Encoding.Unicode.GetString(plainText, 0, decryptedCount);
return decryptedData;
}
private string getMd5Sum(string inputText)
{
// First we need to convert the string into bytes, which
// means using a text encoder.
Encoder encoder = System.Text.Encoding.Unicode.GetEncoder();
// Create a buffer large enough to hold the string
byte[] unicodeText = new byte[inputText.Length * 2];
encoder.GetBytes(inputText.ToCharArray(), 0, inputText.Length, unicodeText, 0, true);
// Now that we have a byte array we can ask the CSP to hash it
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = md5.ComputeHash(unicodeText);
// Build the final string by converting each byte
// into hex and appending it to a StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < result.Length; i++)
{
sb.Append(result[i].ToString("X2"));
}
// And return it
return sb.ToString();
}
private string getSha256Hash(string plainText)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
byte[] saltBytes = Encoding.UTF8.GetBytes(IV);
byte[] plainTextWithSaltBytes =
new byte[plainTextBytes.Length + saltBytes.Length];
for (int i = 0; i < plainTextBytes.Length; i++)
plainTextWithSaltBytes[i] = plainTextBytes[i];
for (int i = 0; i < saltBytes.Length; i++)
plainTextWithSaltBytes[plainTextBytes.Length + i] = saltBytes[i];
HashAlgorithm hash = new SHA256Managed();
byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);
byte[] hashWithSaltBytes = new byte[hashBytes.Length +
saltBytes.Length];
for (int i = 0; i < hashBytes.Length; i++)
hashWithSaltBytes[i] = hashBytes[i];
for (int i = 0; i < saltBytes.Length; i++)
hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];
string hashValue = Convert.ToBase64String(hashWithSaltBytes);
return hashValue;
}
}
}
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using MyCryptographyService.BusinessEntities;
namespace MyCryptographyService.DataAccess
{
///
/// This is the class where all the action happens, and all gathering of external
/// resources (DB, XML files, registry, etc) should be done from here. Ideally, in this
/// scenerio, you would store your keys external to the application and the
/// transformations would be done here with those keys after they were received.
///
/// NOTE: These implementations are derivations (or outright copying) from samples
/// posted on the internet. Included are:
///
/// http://blog.stevex.net/index.php/c-code-snippet-creating-an-md5-hash-string/
/// http://www.codeproject.com/KB/cs/NET_Encrypt_Decrypt.aspx
/// http://www.obviex.com/samples/hash.aspx
///
public class CryptManager
{
// Note: This is not a tutorial on encryption. Key management
// is paramount in good encryption and of course storing your
// key in plain text in the code is not a good idea. This is
// merely sample code and should not be considered good
// cryptography practice.
private const string KEY = "$0ftw@r3";
private const string IV = "@p3t30n&";
private DESCryptoServiceProvider des = new DESCryptoServiceProvider();
public CryptManager()
{
des.Key = ASCIIEncoding.ASCII.GetBytes(KEY);
des.IV = ASCIIEncoding.ASCII.GetBytes(IV);
}
public string HashString(HashObject hashObject)
{
switch (hashObject.HashType)
{
case HashType.MD5:
return getMd5Sum(hashObject.StringToHash);
case HashType.SHA256:
return getSha256Hash(hashObject.StringToHash);
default:
throw new NotImplementedException("That Hashing Algorithm is not supported");
}
}
public string EncryptString(EncryptionObject encryptionObject)
{
string cryptText = string.Empty;
switch (encryptionObject.EncryptionAlgorithm)
{
case EncryptionAlgorithm.DES:
cryptText = encryptDes(encryptionObject.Text);
break;
case EncryptionAlgorithm.Rijndael:
cryptText = encryptRijndael(encryptionObject.Text);
break;
default:
throw new NotImplementedException("You provided an algorithm that is not implemented.");
}
return cryptText;
}
public string DecryptString(EncryptionObject encryptionObject)
{
string plainText = string.Empty;
switch (encryptionObject.EncryptionAlgorithm)
{
case EncryptionAlgorithm.DES:
plainText = decryptDes(encryptionObject.Text);
break;
case EncryptionAlgorithm.Rijndael:
plainText = decryptRijndael(encryptionObject.Text);
break;
default:
throw new NotImplementedException("You provided an algorithm that is not implemented.");
}
return plainText;
}
private string encryptDes(string plainText)
{
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, des.CreateEncryptor(), CryptoStreamMode.Write);
StreamWriter writer = new StreamWriter(cryptoStream);
writer.Write(plainText);
writer.Flush();
cryptoStream.FlushFinalBlock();
writer.Flush();
return Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
private string decryptDes(string cryptText)
{
MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(cryptText));
CryptoStream cryptoStream = new CryptoStream(memoryStream, des.CreateDecryptor(), CryptoStreamMode.Read);
StreamReader reader = new StreamReader(cryptoStream);
return reader.ReadToEnd();
}
private string encryptRijndael(string plainText)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
byte[] plainBytes = Encoding.Unicode.GetBytes(plainText);
byte[] salt = Encoding.ASCII.GetBytes(KEY.Length.ToString());
//This class uses an extension of the PBKDF1 algorithm defined in the PKCS#5 v2.0
//standard to derive bytes suitable for use as key material from a password.
//The standard is documented in IETF RRC 2898.
PasswordDeriveBytes secretKey = new PasswordDeriveBytes(KEY, salt);
//Creates a symmetric encryptor object.
ICryptoTransform encryptor = rijndaelCipher.CreateEncryptor(secretKey.GetBytes(32), secretKey.GetBytes(16));
MemoryStream memoryStream = new MemoryStream();
//Defines a stream that links data streams to cryptographic transformations
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainBytes, 0, plainBytes.Length);
//Writes the final state and clears the buffer
cryptoStream.FlushFinalBlock();
byte[] cipherBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
string encryptedData = Convert.ToBase64String(cipherBytes);
return encryptedData;
}
private string decryptRijndael(string cipherText)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
byte[] encryptedData = Convert.FromBase64String(cipherText);
byte[] salt = Encoding.ASCII.GetBytes(KEY.Length.ToString());
//Making of the key for decryption
PasswordDeriveBytes secretKey = new PasswordDeriveBytes(KEY, salt);
//Creates a symmetric Rijndael decryptor object.
ICryptoTransform decryptor = rijndaelCipher.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16));
MemoryStream memoryStream = new MemoryStream(encryptedData);
//Defines the cryptographics stream for decryption.THe stream contains decrpted data
CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
byte[] plainText = new byte[encryptedData.Length];
int decryptedCount = cryptoStream.Read(plainText, 0, plainText.Length);
memoryStream.Close();
cryptoStream.Close();
//Converting to string
string decryptedData = Encoding.Unicode.GetString(plainText, 0, decryptedCount);
return decryptedData;
}
private string getMd5Sum(string inputText)
{
// First we need to convert the string into bytes, which
// means using a text encoder.
Encoder encoder = System.Text.Encoding.Unicode.GetEncoder();
// Create a buffer large enough to hold the string
byte[] unicodeText = new byte[inputText.Length * 2];
encoder.GetBytes(inputText.ToCharArray(), 0, inputText.Length, unicodeText, 0, true);
// Now that we have a byte array we can ask the CSP to hash it
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = md5.ComputeHash(unicodeText);
// Build the final string by converting each byte
// into hex and appending it to a StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < result.Length; i++)
{
sb.Append(result[i].ToString("X2"));
}
// And return it
return sb.ToString();
}
private string getSha256Hash(string plainText)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
byte[] saltBytes = Encoding.UTF8.GetBytes(IV);
byte[] plainTextWithSaltBytes =
new byte[plainTextBytes.Length + saltBytes.Length];
for (int i = 0; i < plainTextBytes.Length; i++)
plainTextWithSaltBytes[i] = plainTextBytes[i];
for (int i = 0; i < saltBytes.Length; i++)
plainTextWithSaltBytes[plainTextBytes.Length + i] = saltBytes[i];
HashAlgorithm hash = new SHA256Managed();
byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);
byte[] hashWithSaltBytes = new byte[hashBytes.Length +
saltBytes.Length];
for (int i = 0; i < hashBytes.Length; i++)
hashWithSaltBytes[i] = hashBytes[i];
for (int i = 0; i < saltBytes.Length; i++)
hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];
string hashValue = Convert.ToBase64String(hashWithSaltBytes);
return hashValue;
}
}
}
在最后完成本部分演练之前,我们还需要做一件事,我们需要将实现的方法联系起来,这将是我们的endpoints(节点)。当我们确定了我们的服务契约,我们定义了一些方法。我们必须实现这些方法,用我们的业务层配合他们,并最终实现我们的数据访问层。
然而,我们每次对服务契约的设计所做任何更改,由于生成的代码将因覆盖而改变,因此我们必须创建另一个partial class代码文件。根据我们自己的实现情况,重写其中的方法,根据提交的请求实现并返回结果。现在MyCryptographyService.ServiceImplementation项目下的CryptImplementation.cs 。文件中,你应该有以下的代码。
using MyCryptographyService.BusinessLogic;
using MyCryptographyService.MessageContracts;
namespace MyCryptographyService.ServiceImplementation
{
public partial class CryptService : CryptServiceBase
{
protected CryptographyServiceBusinessLogic businessLogic = new CryptographyServiceBusinessLogic();
public override EncryptStringResponse EncryptString(EncryptStringRequest request)
{
EncryptStringResponse response = new EncryptStringResponse();
response.ReturnValue = businessLogic.EncryptString(
EncryptionObjectTranslator.TranslateEncryptionObjectToEncryptionObject(request.EncryptionObject));
return response;
}
public override DecryptStringResponse DecryptString(DecryptStringRequest request)
{
DecryptStringResponse response = new DecryptStringResponse();
response.ReturnValue = businessLogic.DecryptString(
EncryptionObjectTranslator.TranslateEncryptionObjectToEncryptionObject(request.EncryptionObject));
return response;
}
public override HashStringResponse HashString(HashStringRequest request)
{
HashStringResponse response = new HashStringResponse();
response.ReturnValue = businessLogic.HashString(
HashObjectTranslator.TranslateHashObjectToHashObject(request.HashObject));
return response;
}
}
}
我们使用Translators的方法创建一些代码,他们对业务实体和数据契约进行了映射。代码应该是非常简单,但如果您有任何问题,请在此留下您的意见,我会尽快答复。
在演练的第4部分,我们会创建托管主机,部署和测试WSDL。敬请关注。