asp.net 2.0中新增的最佳功能之一是新的成員身份服務(wù),它提供了用于創(chuàng)建和管理用戶帳戶的易于使用的api。asp.net 1.x大規(guī)模引入了窗體身份驗證,但仍然要求您編寫相當(dāng)數(shù)量的代碼來執(zhí)行實際操作中的窗體身份驗證。成員身份服務(wù)填補了asp.net 1.x窗體身份驗證服務(wù)的不足,并且使實現(xiàn)窗體身份驗證變得比以前簡單得多。
成員身份api通過兩個新的類公開:membership和membershipuser。前者包含了用于創(chuàng)建用戶、驗證用戶以及完成其他工作的靜態(tài)方法。membershipuser代表單個用戶,它包含了用于檢索和更改密碼、獲取上次登錄日期以及完成類似工作的方法和屬性。通過這兩個新的類,我們可以不用寫一行代碼,方便得完成對用戶的管理。 但是在實際開發(fā)過程中,絕對不能滿足我們?nèi)粘i_發(fā)的需要。經(jīng)過日常項目的開發(fā)和網(wǎng)絡(luò)上資料的搜索,現(xiàn)將其一一列出:
一、默認的各類的數(shù)據(jù)庫是使用sql express的,而我們在實際開發(fā)中往往使用sql sever 2000或者sql server 2005,這時就需要我們修改數(shù)據(jù)庫的類型。
微軟給我們提供了一個aspnet_regsql的命令來修改默認數(shù)據(jù)庫。打開 visual studio 2005 命令提示,輸入aspnet_regsql,按照提示一步一步進行即可。
此時打開數(shù)據(jù)庫,可以發(fā)現(xiàn)多處來了一系列"aspnet_"開頭的存儲過程,這就是我們使用membership所必需的存儲過程。
此時打開iis,[屬性] →[asp.net] →[編輯配置]:
[常規(guī)],連接參數(shù)localsqlserver按照普通的sql連接字符串格式。
[身份驗證],模式為forms,管理提供程序的minrequirednonalphanumericcharacters為0,這時就可以去掉默認變態(tài)的必需需要輸入字母,數(shù)字等組合的密碼安全了。此步也可修改密碼最低長度和最大長度等等。
經(jīng)過此步驟,系統(tǒng)會自動在web.config中配置好了我們所需的規(guī)則。很方便,修改web.config以后都可以通過這種圖形化工具來了。
二、由于自帶的login控件和membership類,只提供了簡單的用戶信息錄入,不能滿足我們項目的需要。例如:我們要用戶注冊的時候同時輸入qq號碼,電話號碼,家庭地址。那么默認的是沒有辦法解決的。我這里給出兩種解決方案。我分別用在了不同的項目中。優(yōu)缺點大家自行判斷。
1、使用profile。此類方法網(wǎng)上教程已經(jīng)很多。不在出重復(fù)敘述,免去賺稿費的嫌疑:)。這里只是給出網(wǎng)上沒有的部分說明。
由于membership只能列出來指定組的用戶名,而不能列出其他的詳細信息,我們實際使用中,往往需要對組中的其它信息進行同時修改。我采用的是自行構(gòu)造datatable的方法。見代碼:
public static datatable listuser(string userroles)//列出指定組的用戶信息
{
string[] users = roles.getusersinrole(userroles);
//列出指定組下的用戶
datatable dt = new datatable();
dt.columns.add("username", system.type.gettype("system.string"));
dt.columns.add("qq", system.type.gettype("system.string"));
dt.columns.add("phone", system.type.gettype("system.string"));
dt.columns.add("address", system.type.gettype("system.string"));
dt.columns.add("email", system.type.gettype("system.string"));
//以上構(gòu)造一個數(shù)據(jù)表
foreach (string i in users)
{
datarow dr = dt.newrow();
membershipuser mu = membership.getuser(i);
得到用戶基本信息
profilecommon p = profile.getprofile(i); //得到用戶的profile信息
dr[0] = mu. username;
dr[1] = p. qq;
//profile是強類型,可以很方便的通過感知來添加。
dr[2] = p. phone;
dr[3] = p. address;
dr[4] = mu. email;
dt.rows.add(dr);
dt.acceptchanges();
}
return dt;
}
public static void deleteuser(string username)/刪除指定用戶
{
membership.deleteuser(username);
//系統(tǒng)會自動刪除profile下的指定用戶的信息
}
public static void updateuser(string username)/更新指定用戶
{
profilecommon p = profile.getprofile(i);
//得到用戶的profile信息
p. phone="電話";
p. address="地址;
p. qq="qq號碼";
p.save();
//保存所作修改。
}
2、自定義一個membershipinfo表格,同membership系統(tǒng)標(biāo)關(guān)聯(lián)起來。自己編寫sql語句來進行查詢,修改等功能。
列出指定組的用戶
select * from aspnet_membership inner join aspnet_users on
aspnet_membership.userid=aspnet_users.userid left join memberinfo on aspnet_membership.userid=memberinfo.userid
where aspnet_membership.userid=(select userid from aspnet_usersinroles inner join
aspnet_roles on aspnet_usersinroles.roleid=aspnet_roles.roleid where rolename='admin')
刪除、修改等功能比較簡單,這里就不作敘述。可以采用membership的createuser方法建立,然后再用sql語句寫入memberinfo表。
對于建立用戶,我們可以采取擴展createuserwizard控件,或者自行寫登陸界面。
1、采取擴展createuserwizard控件,我們可以使用它的模版列,此時需要注意的是:用戶名,密碼,提示問題,提示問題答案,email,他們的id一定要分別是username,password,question,answer,email否則會出錯,而且此時驗證控件均不能使用。懷疑是ide的一個bug。
如下所示,我們定義好的樣式應(yīng)當(dāng)是:
<wizardsteps>
<asp:createuserwizardstep runat="server"> 自定義代碼部分<contenttemplate>
</contenttemplate>
</asp:createuserwizardstep>
</wizardsteps>
代碼部分:
protected void createuserwizard1_createduser(object sender, eventargs e)
{
//由于系統(tǒng)會自動給們建立基本的信息表,所以我們只需要對profile或者membershipinfo標(biāo)進行修改即可。
roles.addusertorole(createuserwizard1.username, "shop");
//添加用戶到相應(yīng)的組
profilecommon p = (profilecommon)profilecommon.create(createuserwizard1.username, true);
p. qq = ((textbox)createuserwizard1.createuserstep.contenttemplatecontainer.findcontrol("qq")).text.trim();
p.address= ((textbox)createuserwizard1.createuserstep.contenttemplatecontainer.findcontrol("address")).text.trim();
p.phone = ((textbox)createuserwizard1.createuserstep.contenttemplatecontainer.findcontrol("phone")).text.trim(); p.save();//保存所作修改
}
2、采取自己寫ui,個人推薦使用這種方法。靈活性比較大,而且可以使用2005強大的驗證控件。
頁面部分比較簡單略過不再陳述。下面詳細介紹一下代碼部分。
此時我們需要使用membership的createuser()方法。語法我們就不再介紹。他會根據(jù)建立用戶的結(jié)果返回成一個membershipcreatestatus枚舉類,它詳細的包含了所有建立用戶不成功的錯誤信息。我們只需要根據(jù)他的值,就可以返回給界面相應(yīng)的提示,如:用戶名已經(jīng)存在,電子郵件已經(jīng)存在等等。
對于自定義用戶信息部分我們?nèi)匀豢梢圆扇rofile或者自定義membershipinfo表的方法。
列出profile建立用戶的方法。sql語句比較簡單,略過不寫。
protected void button1_click(object sender, eventargs e)
{
membershipcreatestatus status;
membershipuser newuser = membership.createuser(username.text.trim(), password.text.trim(), email.text.trim(), question.text.trim(), answer.text.trim(), true, out status);
//使用membership建立用戶,并把建立結(jié)果返回給membershipcreatestatus
if (newuser == null)//沒有新用戶,則意味著出錯。
{
geterrormessage(status);
//調(diào)用geterrormessage函數(shù),返回詳細錯誤信息
} else {
roles.addusertorole(newuser.username, "jiancai");
//添加用戶到相應(yīng)組 profilecommon
p = (profilecommon)profilecommon.create(newuser.username, true);
p.qq = qq.text.trim();
p.address= address.text.trim();
p.phone= phone.text.trim();
p.save();
}
}
public void geterrormessage(membershipcreatestatus status)
{
switch (status)
{
case membershipcreatestatus.duplicateusername: displayalert("當(dāng)前用戶已經(jīng)存在,請重新選擇");
break;
//其余各種錯誤信息,請查看msdn的membershipcreatestatus枚舉類。
default: displayalert("注冊00000000用戶失敗,請檢查您的用戶名,密碼等信息");
break;
}
}
個人見解:采取profile的方法,比較方便,由于profile 是強類型,可以通過智能感知功能減少代碼的輸入量。采取自定義數(shù)據(jù)表的方法,需要輸入大量的sql語句,但是查詢速度比較快,性能比較強,由于roles.getusersinrole()方法無法分頁讀取數(shù)據(jù),只能一次性讀出來所有數(shù)據(jù),而自寫sql 語句可以很方便的根分頁結(jié)合起來。隨著用戶量的增多,故不推薦profile方法。 三、密碼問題。
個人覺得密碼是一個比較頭疼的問題。我們在實際開發(fā)中總是需要admin組有對用戶進行密碼修改的權(quán)限。membership提供的修改密碼方法只能在已經(jīng)知道密碼提示答案的時候才能修改。而admin組根本不可能知道用戶的密碼提示答案的。這里有點好笑。難道是中西方文化差異?
列出個人對membership密碼研究的一些心得。
大家都知道m(xù)achine.config和web.config,如果兩者發(fā)生沖突,那么以web.config優(yōu)先。
默認狀態(tài)下,membership是采用sha1的方法進行加密,然后采取一種機制,與passwordsalt進行再次加密,最后形成數(shù)據(jù)庫中顯示的密碼。可見隨著md5加密的破解,微軟對密碼的安全也煞費苦心。
有的朋友不喜歡默認的sha1加密形式,我們只需要在web.config中設(shè)置以下代碼來覆蓋machine.config中對密碼的設(shè)置就好了:
<machinekey
validationkey="autogenerate,isolateapps"
decryptionkey="autogenerate,isolateapps"
decryption="auto"
validation="md5/sha1/clear" />
其中:
clear - 密碼以明文形式存儲。用戶密碼可與此值直接比較,而不需要進行進一步的轉(zhuǎn)換。
md5 -使用消息摘要 5 (md5) 哈希摘要存儲密碼。為了驗證憑據(jù),將使用 md5 算法對用戶密碼進行哈希運算并將計算出來的值與存儲的值進行比較。使用此值時,從不存儲或比較明文密碼。此算法的性能比 sha1 好。
sha1 -使用 sha1 哈希摘要存儲密碼。為了驗證憑據(jù),將使用 sha1 算法對用戶密碼進行哈希運算并將計算出來的值與存儲的值進行比較。從不存儲明文密碼。使用該算法可以獲得比 md5 算法高的安全性。
然而雖然membershipuser提供了getpassword 方法,但是這是后只有在加密形式設(shè)置成clear,即密碼在數(shù)據(jù)庫中以明碼的形式存在,才能得到密碼。而changepassword必須要提供舊密碼或者密碼提示答案才可以修改。對用戶管理造成了很大的不便。找了許多資料無解。順便提帶一下:微軟的membership機制起源于csblog,并進行了一定的修改。
csblog對密碼的策略:首先對用戶輸入的密碼進行加密(sha1或者md5),然后根據(jù)系統(tǒng)自動生成的密匙再次對加密后的密碼進行des加密。不過我按照csblog 的方法進行加密的時候,形式相同,密碼卻不同,目前正在演就中。也歡迎大家共同討論。
如果passwordsalt和password固定,那么用戶密碼肯定一定。所以目前我采取的一個方法就是給用戶重新設(shè)置成固定密碼的功能。
首先取得已知用戶密碼的passwordsalt和password,然后替換相應(yīng)用戶的passwordsalt和password字段。這時,用戶的密碼就已經(jīng)恢復(fù)成已知的密碼了。
不可否認,membership給我們開發(fā)中創(chuàng)造了很大的便利,其方便的roles功能,對于我們進行權(quán)限管理的時候提供了很好的解決方案。2005許多功能均進化自csblog這優(yōu)秀的開源項目,有興趣者可以研究csblog,以進一步的了解membership的運作機制。限于篇幅,msdn上邊有做介紹的我就不再重復(fù)敘述。只介紹msdn上沒有列出的技巧跟我在項目開發(fā)中的一些心得。 asp.net 2.0為使用窗體身份驗證的web站點提供了重要的安全性優(yōu)勢。通過提供用戶配置文件儲備庫以及對角色的支持,窗體身份驗證將走出asp.net內(nèi)行的視野,而得以更廣泛地實現(xiàn)。菜鳥學(xué)堂: