[转]自定义SqlMembershipProvider方法
asp 2.0自帶了許多方法,很多東西都給我們封裝好了,但是這樣就導(dǎo)致了我們自定義的空間越來(lái)越小。忙碌了兩個(gè)星期,一直想重寫System.Web.Security.SqlMembershipProvider,但是沒(méi)有找到方法,昨天在asp.net這個(gè)晚上上找到了解決方法,真是很興奮。
打下可以參考一下http://forums.asp.net/p/1042049/1458049.aspx#1458049,我的大部分內(nèi)容是按照里面的方法來(lái)寫的。然后將中途遇到的問(wèn)題做一部分解釋。
步驟一:
首先下載一個(gè)ProviderToolkitSamples,下載鏈接如下:
http://msdn.microsoft.com/en-us/library/aa478948.aspx。
因?yàn)槲覀円貙慡qlMembershipProvider,好多要用到很多.net 2.0底層的方法,這些方法是首先的,就是我們不能直接使用,比如我們要在類的前面定義:using System.Web.SR就會(huì)報(bào)錯(cuò),錯(cuò)誤如下:'System.Web.SR' is inaccessible due to its protection level。既然我們要重寫SqlMembershipProvider,那么我們就要提供這個(gè)類中所使用的所有方法。
步驟二:
安裝ProviderToolkitSamples,在C:\Program Files\ASP.NET Provider Toolkit SQL Samples目錄下找到下面的四個(gè)類,分別是:
SecUtil.cs,SqlConnectionHelper.cs,SQLMembershipProvider.cs,SR.cs。將他們拷貝打網(wǎng)站項(xiàng)目的app_code文件夾下面。
因?yàn)樵瓉?lái)已經(jīng)有了.vb的程序集,我們這個(gè)時(shí)候如果將.cs文件放在同一個(gè)文件夾下會(huì)編譯錯(cuò)誤,解決方法參考我前面的那一片博文App_Code目錄中存放不同語(yǔ)言的類文件導(dǎo)致錯(cuò)誤的解決方案。
還有就是將SQLMembershipProvider.cs改名為MySQLMembershipProvider.cs,并且在類定義做如下修改
view plaincopy to clipboardprint?
?public class MySQLMembershipProvider : System.Web.Security.MembershipProvider??
{??
/??
}?
?public class MySQLMembershipProvider : System.Web.Security.MembershipProvider
{
/
}
步驟三:
重新定義一個(gè)比較方便的命名空間,然后對(duì)web.config做一些修改,我的修改如下:
view plaincopy to clipboardprint?
<add name="MySQLMembershipProvider"???
????? type="MyProviders.MySQLMembershipProvider"???
????? connectionStringName="SiteSqlServer"?
????? enablePasswordRetrieval="false"???
????? enablePasswordReset="true"???
????? requiresQuestionAndAnswer="false"?
????? minRequiredPasswordLength="3"?
????? minRequiredNonalphanumericCharacters="0"?
????? requiresUniqueEmail="false"???
????? passwordFormat="Hashed"???
????? applicationName="DotNetNuke"???
????? description="使用MVPHacksMembershipProvider"/>?
? <add name="MySQLMembershipProvider"
??????? type="MyProviders.MySQLMembershipProvider"
??????? connectionStringName="SiteSqlServer"
??????? enablePasswordRetrieval="false"
??????? enablePasswordReset="true"
??????? requiresQuestionAndAnswer="false"
??????? minRequiredPasswordLength="3"
??????? minRequiredNonalphanumericCharacters="0"
??????? requiresUniqueEmail="false"
??????? passwordFormat="Hashed"
??????? applicationName="DotNetNuke"
??????? description="使用MVPHacksMembershipProvider"/>
從上面可以看出的命名空間是MyProviders,MyProviders是來(lái)自MySQLMembershipProvider.cs文件中的
view plaincopy to clipboardprint?
namespace MyProviders???
{??
///??
}?
namespace MyProviders
{
///
}
接著更改membership defaultProvider,如下:
view plaincopy to clipboardprint?
<membership defaultProvider="MySQLMembershipProvider" userIsOnlineTimeWindow="15">?
?<membership defaultProvider="MySQLMembershipProvider" userIsOnlineTimeWindow="15">
步驟四:
對(duì)MySQLMembershipProvider進(jìn)行自定義修改。這里首先說(shuō)一下, 我們從前面的
public class MySQLMembershipProvider : System.Web.Security.MembershipProvider{}
就可以看出我們自定義的MySQLMembershipProvider 繼承了System.Web.Security.MembershipProvider而不是System.Web.Security.SqlMembershipProvider。但是我們的代碼確實(shí)SqlMembershipProvider,這樣就給了我們完全的自定義空間,想想就讓人興奮。
我這次項(xiàng)目的目標(biāo)是將已有項(xiàng)目的加密方式改成16位的MD5加密方式,一次我在MySQLMembershipProvider類里面添加了一個(gè)求md5的方法,代碼如下:
view plaincopy to clipboardprint?
//注:自加求md5密碼的靜態(tài)方法??
public static string GetMd5(string str)//求MD5?????
{??
??? return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(str, "MD5").ToLower().Substring(8,16);??
}?
??????? //注:自加求md5密碼的靜態(tài)方法
??????? public static string GetMd5(string str)//求MD5??
??????? {
??????????? return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(str, "MD5").ToLower().Substring(8,16);
??????? }
我們知道m(xù)d5加密時(shí)單向的,這和SqlMembershipProvider中的Hashed加密方式類似,所以我想到的是直接將Hashed改寫為md5類型,這樣其他原封不動(dòng),修改工作量大大減少。
下面來(lái)討論加密方式,我們?cè)谠创a中可以找到
view plaincopy to clipboardprint?
string salt = GenerateSalt();??
??????? string pass = EncodePassword(password, (int)_PasswordFormat, salt);?
??? string salt = GenerateSalt();
??????????? string pass = EncodePassword(password, (int)_PasswordFormat, salt);
這表明加密方式是由EncodePassword(string pass, int passwordFormat, string salt)方法來(lái)控制的,下面是我修改后的代碼
view plaincopy to clipboardprint?
internal string EncodePassword(string pass, int passwordFormat, string salt)??
?{??
???? if (passwordFormat == 0) // MembershipPasswordFormat.Clear??
???????? return pass;??
?
???? byte[] bIn = Encoding.Unicode.GetBytes(pass);??
???? byte[] bSalt = Convert.FromBase64String(salt);??
???? byte[] bAll = new byte[bSalt.Length + bIn.Length];??
???? byte[] bRet = null;??
?
???? Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);??
???? Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);??
???? if (passwordFormat == 1)??
???? { // MembershipPasswordFormat.Hashed??
???????? HashAlgorithm s = HashAlgorithm.Create( Membership.HashAlgorithmType );??
???????? bRet = s.ComputeHash(bAll);??
???????? //手動(dòng)添加??
???????? return GetMd5(pass);??
???? } else?
???? {??
???????? bRet = EncryptPassword( bAll );??
???? }??
?
???? return Convert.ToBase64String(bRet);??
?}?
?????? internal string EncodePassword(string pass, int passwordFormat, string salt)
??????? {
??????????? if (passwordFormat == 0) // MembershipPasswordFormat.Clear
??????????????? return pass;
??????????? byte[] bIn = Encoding.Unicode.GetBytes(pass);
??????????? byte[] bSalt = Convert.FromBase64String(salt);
??????????? byte[] bAll = new byte[bSalt.Length + bIn.Length];
??????????? byte[] bRet = null;
??????????? Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
??????????? Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
??????????? if (passwordFormat == 1)
??????????? { // MembershipPasswordFormat.Hashed
??????????????? HashAlgorithm s = HashAlgorithm.Create( Membership.HashAlgorithmType );
??????????????? bRet = s.ComputeHash(bAll);
??????????????? //手動(dòng)添加
??????????????? return GetMd5(pass);
??????????? } else
??????????? {
??????????????? bRet = EncryptPassword( bAll );
??????????? }
??????????? return Convert.ToBase64String(bRet);
??????? }
這樣只要我們?cè)谂渲脀eb.config的時(shí)候使用Hashed加密方式,得到的就是MD5的密碼。
步驟五
下面我們要做的就是如何修改系統(tǒng)中原有賬戶的密碼。
首先我們?cè)贒NN中注冊(cè)一個(gè)用戶,假設(shè)我們注冊(cè)賬號(hào)和密碼都是testmd5,那么這個(gè)時(shí)候我們?nèi)?shù)據(jù)庫(kù)中查看會(huì)發(fā)現(xiàn)有一個(gè)密碼為
"3a25306bb46a03d6"的賬戶,這個(gè)就是我們新建的testmd5密碼的16位md5密碼。
我們通過(guò)下面的腳本來(lái)更改host的密碼,讓其密碼也成為testmd5.view plaincopy to clipboardprint?
/*?
?-- Database Utility ---------------------------------------------------------------------------?
?Description : Reset a Password in a DotNetNuke database?
?Author : Tony Tullemans?
?Date Created : 18.04.2007?
?Note/s : Before you run this script you must know the UserName and Password of another?
?registered DNN user in the database you wish to affect.?
?-----------------------------------------------------------------------------------------------?
?*/?
???
DECLARE @databaseName VARCHAR(128)??
SELECT @databaseName = DB_NAME()??
???
PRINT 'RESET PASSWORD IN DATABASE : ' + @databaseName??
PRINT '-----------------------------' + REPLICATE('-', DATALENGTH(@databaseName ));??
???
DECLARE @knownUserName NVARCHAR(128)??
DECLARE @lostUserName NVARCHAR(128)??
DECLARE @lostUserId NVARCHAR(128)??
DECLARE @knownPassword NVARCHAR(128)??
DECLARE @knownSalt NVARCHAR(128)??
???
SET @knownUserName = 'host'?
SET @lostUserName = 'testmd5'?
?
SELECT @knownPassword = Password, @knownSalt = PasswordSalt??
FROM aspnet_Membership??
INNER JOIN aspnet_users??
ON aspnet_Membership.UserId = aspnet_users.UserId??
where UserName = @knownUserName;??
???
PRINT ''?
PRINT 'Known Password for "' + @knownUserName + '" is : ' + @knownPassword??
PRINT 'Known Password Salt for "' + @knownUserName + '" is : ' + @knownSalt??
???
SELECT @lostUserId = aspnet_Membership.UserId??
FROM aspnet_Membership??
INNER JOIN aspnet_users??
ON aspnet_Membership.UserId = aspnet_users.UserId??
WHERE UserName = @lostUserName;??
???
PRINT ''?
PRINT 'UserID for "' + @lostUserName + '" is : ' + @lostUserId??
PRINT ''?
???
IF (DATALENGTH(@lostUserName) <= 0 OR @lostUserName IS NULL)??
PRINT 'Invalid Lost User Name ' + @lostUserName??
ELSE BEGIN??
IF (DATALENGTH(@knownUserName) <= 0 OR @knownUserName IS NULL)??
PRINT 'Invalid Lost User Name ' + @lostUserName??
ELSE BEGIN??
IF (DATALENGTH(@knownPassword) <= 0 OR @knownPassword IS NULL)??
PRINT 'Invalid Known Password ' + @knownPassword??
ELSE BEGIN??
IF (DATALENGTH(@knownSalt) <= 0 OR @knownSalt IS NULL)??
PRINT 'Invalid Known Salt ' + @knownSalt??
ELSE BEGIN??
PRINT ''?
PRINT 'BEFORE'?
SELECT left(UserName, 12) as UserName, aspnet_Membership.UserId, left(Email, 20) as Email, Password, PasswordSalt??
FROM aspnet_Membership INNER JOIN aspnet_users ON aspnet_Membership.UserId = aspnet_users.UserId??
WHERE UserName IN ( @knownUserName, @lostUserName );??
PRINT ''?
PRINT 'Changing Password for User Id : "' + @lostUserId + '" to "' + @knownPassword + '"'?
PRINT ''?
UPDATE aspnet_Membership??
SET Password = @knownPassword,??
PasswordSalt = @knownSalt??
-- SELECT UserId, Password, PasswordSalt??
-- FROM aspnet_Membership??
WHERE UserId = @lostUserId;??
PRINT ''?
PRINT 'AFTER'?
SELECT left(UserName, 12) as UserName, aspnet_Membership.UserId, left(Email, 20) as Email, Password, PasswordSalt??
FROM aspnet_Membership INNER JOIN aspnet_users ON aspnet_Membership.UserId = aspnet_users.UserId??
WHERE UserName IN ( @knownUserName, @lostUserName );??
END??
END??
END??
END??
GO??
?
PRINT ''?
PRINT ' * * * END OF SCRIPT * * *'?
PRINT ''?
GO?
/*
?-- Database Utility ---------------------------------------------------------------------------
?Description : Reset a Password in a DotNetNuke database
?Author : Tony Tullemans
?Date Created : 18.04.2007
?Note/s : Before you run this script you must know the UserName and Password of another
?registered DNN user in the database you wish to affect.
?-----------------------------------------------------------------------------------------------
?*/
?
DECLARE @databaseName VARCHAR(128)
SELECT @databaseName = DB_NAME()
?
PRINT 'RESET PASSWORD IN DATABASE : ' + @databaseName
PRINT '-----------------------------' + REPLICATE('-', DATALENGTH(@databaseName ));
?
DECLARE @knownUserName NVARCHAR(128)
DECLARE @lostUserName NVARCHAR(128)
DECLARE @lostUserId NVARCHAR(128)
DECLARE @knownPassword NVARCHAR(128)
DECLARE @knownSalt NVARCHAR(128)
?
SET @knownUserName = 'host'
SET @lostUserName = 'testmd5'
SELECT @knownPassword = Password, @knownSalt = PasswordSalt
FROM aspnet_Membership
INNER JOIN aspnet_users
ON aspnet_Membership.UserId = aspnet_users.UserId
where UserName = @knownUserName;
?
PRINT ''
PRINT 'Known Password for "' + @knownUserName + '" is : ' + @knownPassword
PRINT 'Known Password Salt for "' + @knownUserName + '" is : ' + @knownSalt
?
SELECT @lostUserId = aspnet_Membership.UserId
FROM aspnet_Membership
INNER JOIN aspnet_users
ON aspnet_Membership.UserId = aspnet_users.UserId
WHERE UserName = @lostUserName;
?
PRINT ''
PRINT 'UserID for "' + @lostUserName + '" is : ' + @lostUserId
PRINT ''
?
IF (DATALENGTH(@lostUserName) <= 0 OR @lostUserName IS NULL)
PRINT 'Invalid Lost User Name ' + @lostUserName
ELSE BEGIN
IF (DATALENGTH(@knownUserName) <= 0 OR @knownUserName IS NULL)
PRINT 'Invalid Lost User Name ' + @lostUserName
ELSE BEGIN
IF (DATALENGTH(@knownPassword) <= 0 OR @knownPassword IS NULL)
PRINT 'Invalid Known Password ' + @knownPassword
ELSE BEGIN
IF (DATALENGTH(@knownSalt) <= 0 OR @knownSalt IS NULL)
PRINT 'Invalid Known Salt ' + @knownSalt
ELSE BEGIN
PRINT ''
PRINT 'BEFORE'
SELECT left(UserName, 12) as UserName, aspnet_Membership.UserId, left(Email, 20) as Email, Password, PasswordSalt
FROM aspnet_Membership INNER JOIN aspnet_users ON aspnet_Membership.UserId = aspnet_users.UserId
WHERE UserName IN ( @knownUserName, @lostUserName );
PRINT ''
PRINT 'Changing Password for User Id : "' + @lostUserId + '" to "' + @knownPassword + '"'
PRINT ''
UPDATE aspnet_Membership
SET Password = @knownPassword,
PasswordSalt = @knownSalt
-- SELECT UserId, Password, PasswordSalt
-- FROM aspnet_Membership
WHERE UserId = @lostUserId;
PRINT ''
PRINT 'AFTER'
SELECT left(UserName, 12) as UserName, aspnet_Membership.UserId, left(Email, 20) as Email, Password, PasswordSalt
FROM aspnet_Membership INNER JOIN aspnet_users ON aspnet_Membership.UserId = aspnet_users.UserId
WHERE UserName IN ( @knownUserName, @lostUserName );
END
END
END
END
GO
PRINT ''
PRINT ' * * * END OF SCRIPT * * *'
PRINT ''
GO
上面的腳本在網(wǎng)上挺流行的,大家可能都能找到。
最后還有一步,把數(shù)據(jù)庫(kù)中passwordformat改成1,因?yàn)樵瓉?lái)hashed默認(rèn)的就是1.現(xiàn)在用md5替換hashed,那么就應(yīng)該是1.
結(jié)語(yǔ)
奮戰(zhàn)兩個(gè)星期,終于解決問(wèn)題,其實(shí)問(wèn)題并不難,很早以前就有人有同樣的問(wèn)題,而且早已被解決,難的如何找到解決問(wèn)題的方法,如何找到資源。在這中間我發(fā)現(xiàn)好多都是英文資料,中文資料少之又少。這時(shí)候終于體現(xiàn)了英語(yǔ)的作用了。
致謝
john,M2Land,從不閑聊,牛哥
?
本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/xw13106209/archive/2010/01/05/5133690.aspx
總結(jié)
以上是生活随笔為你收集整理的[转]自定义SqlMembershipProvider方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Sybase常见问题
- 下一篇: Office2010试用