Alternative to System.Web.Security, using same database
I am writing a WinForms application to migrate a lot of data. The new system is web-based and uses ASP.NET membership API.
I have to use transactions to wrap a lot of DB inserts and updates into a single transaction. This includes updating Users and Roles (aspnet_Users, aspnet_Roles, etc.) I've successfully referenced System.Web.Membership and used it in my app to verify the data before starting the migration, so that's not a problem.
The problem, however, is during migration, when I wrap all DB calls in a single Transaction. Since Membership code closes the connection, I get a DTC error, saying that distributed transactions aren't enabled. I would like to avoid changing anything on the client machine, so I am looking for a way to update Users and Roles with ability to roll back.
Right now, as far as I can tell, my only choice is to call the stored procedures directly, avoiding using Membership API. I would like to avoid this as well, if possible, so I was wondering if there is a way to either use the Membership API within transactions or if there 开发者_JAVA百科is an alternative library that uses the same database tables, but plays nicely with the transactions.
Huge thanks in advance to anyone for any input!
I've ended up calling the stored procedures directly. The only hurdle I encountered was with creating a new user. It requires password and security answer encryption, so for this I simply copied the code from the framework source code. From System.Web.Security.SqlMembershipProvider.cs to be exact.
I've removed and trimmed some of this code to fit my own scenario, since I am only using SHA1 encryption.
I am pretty sure that I am not the only one having this problem, so for anyone else, who has the same issue, here is a somewhat complete code for inserting a user. Other stored procedures are easier to call.
There is almost no error checking here, so add your own and use System.Security.Cryptography
//Call to create a new user and return the ID
public static Guid? CreateUser(MyDataContext DB, string UserName, string Password, string Email, string PasswordQuestion, string PasswordAnswer)
{
string salt = GenerateSalt();
string password = EncodePassword(Password, salt);
string encodedPasswordAnswer = EncodePassword(PasswordAnswer.ToLower(), salt);
DateTime dt = DateTime.UtcNow;
Guid? newUserID = null;
//res would contain the success or fail code from the stored procedure
//0 = success; 1 = fail;
int res = DB.aspnet_Membership_CreateUser( "[My app name]", UserName, password, salt, Email, PasswordQuestion, encodedPasswordAnswer, true, dt, DateTime.Now, 0, 1, ref newUserID);
return newUserID;
}
private static string GenerateSalt()
{
byte[] buf = new byte[16];
(new RNGCryptoServiceProvider()).GetBytes(buf);
return Convert.ToBase64String(buf);
}
private static string EncodePassword(string pass, string salt)
{
byte[] bIn = Encoding.Unicode.GetBytes(pass);
byte[] bSalt = Convert.FromBase64String(salt);
byte[] bRet = null;
HashAlgorithm hm = HashAlgorithm.Create("SHA1");
if (hm is KeyedHashAlgorithm)
{
KeyedHashAlgorithm kha = (KeyedHashAlgorithm)hm;
if (kha.Key.Length == bSalt.Length)
{
kha.Key = bSalt;
}
else if (kha.Key.Length < bSalt.Length)
{
byte[] bKey = new byte[kha.Key.Length];
Buffer.BlockCopy(bSalt, 0, bKey, 0, bKey.Length);
kha.Key = bKey;
}
else
{
byte[] bKey = new byte[kha.Key.Length];
for (int iter = 0; iter < bKey.Length; )
{
int len = Math.Min(bSalt.Length, bKey.Length - iter);
Buffer.BlockCopy(bSalt, 0, bKey, iter, len);
iter += len;
}
kha.Key = bKey;
}
bRet = kha.ComputeHash(bIn);
}
else
{
byte[] bAll = new byte[bSalt.Length + bIn.Length];
Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
bRet = hm.ComputeHash(bAll);
}
return Convert.ToBase64String(bRet);
}
精彩评论