本來(lái)標(biāo)題應(yīng)當(dāng)是,利用.net框架創(chuàng)作安全性網(wǎng)站。
這是從msdn上摘抄整理而來(lái)的,結(jié)合我自己的經(jīng)驗(yàn)之談。
我看了有很多朋友都在嘗試寫(xiě)出帶有登陸這樣功能的網(wǎng)站,其方法幾乎都是驗(yàn)證用戶(hù)的登陸合法,然后發(fā)送一個(gè)表示驗(yàn)證的cookie,或者在session中保存信息以便于追蹤接下來(lái)的訪(fǎng)問(wèn)授權(quán),其實(shí),這些細(xì)節(jié)化的操作,.net都提供了一種非常有效的解決辦法,能使你從繁瑣的安全驗(yàn)證上解脫出來(lái),而且,盡管你可能很小心地定義那些頁(yè)面不能被沒(méi)有權(quán)限的人訪(fǎng)問(wèn),然而還有可能出現(xiàn)一些無(wú)法被檢查出來(lái)的漏洞讓他們跳過(guò)安全驗(yàn)證
好,廢話(huà)少說(shuō),本文將介紹如下內(nèi)容:
1、關(guān)于登陸驗(yàn)證和授權(quán)
2、使用forms驗(yàn)證模式
3、授權(quán)資源的訪(fǎng)問(wèn)
4、基于角色的授權(quán)
1、關(guān)于登陸驗(yàn)證和授權(quán)
    很多網(wǎng)站都有登陸對(duì)話(huà)框,讓事先已經(jīng)注冊(cè)的用戶(hù)驗(yàn)證,以便為他們提供個(gè)性化的服務(wù)等。可以把這個(gè)過(guò)程看作是兩件事情的發(fā)生:驗(yàn)證和授權(quán)!登陸的作用是驗(yàn)證請(qǐng)求登陸的用戶(hù)是否合法,而授權(quán)則是驗(yàn)證合法的用戶(hù)在請(qǐng)求資源時(shí),根據(jù)他們的權(quán)限決定是訪(fǎng)問(wèn)還是拒絕。
    以上這種網(wǎng)站本身提供對(duì)話(huà)框的作法在.net中被稱(chēng)之為forms驗(yàn)證模式,接下來(lái)將會(huì)講述這種驗(yàn)證模式。在以前asp陳序員或者其他程序員,要想保存合法用戶(hù)的驗(yàn)證,在以后的訪(fǎng)問(wèn)授權(quán)中使用,不得不使用寫(xiě)cookie或者將信息保存在session中的方法,而在需要授權(quán)的頁(yè)面加載前添加一堆繁瑣的代碼來(lái)驗(yàn)證制定的用戶(hù)是否具有訪(fǎng)問(wèn)權(quán)限否則的話(huà)就不能顯示頁(yè)面的內(nèi)容,最?lèi)阑鸬氖窃谑跈?quán)頁(yè)面上添加這些代碼讓人覺(jué)得重復(fù)和繁瑣,而且可能不是最安全的,有一些比較隱蔽的方式可能會(huì)輕易繞過(guò)這種驗(yàn)證,因此程序員將來(lái)要做的很多事情就是再修改代碼已堵住在運(yùn)行過(guò)程中才發(fā)現(xiàn)的漏洞。在.net的system.web.security中提供了一些網(wǎng)站安全方面的解決方案,盡管驗(yàn)證用戶(hù)合法和授權(quán)的基本思路沒(méi)有變化,但是授權(quán)的工作幾乎已經(jīng)交給.net框架了,我們些代碼之需要自己驗(yàn)證用戶(hù)合法,并且告訴框架這個(gè)用戶(hù)合法即可。
2、使用forms驗(yàn)證模式
    要使用啟用forms驗(yàn)證模式,請(qǐng)?jiān)诰W(wǎng)站根目錄下的web.config文件中添加如下配置:(注意區(qū)分大小寫(xiě))
<configuration>
  <system.web>
    <authentication mode="forms" />
  </system.web>
</configuration>
    這將告訴.net,你的網(wǎng)站使用forms驗(yàn)證模式,.net將不參與驗(yàn)證用戶(hù)的工作,而是將這個(gè)工作交給你完成,你必須自己編寫(xiě)一些代碼來(lái)驗(yàn)證用戶(hù)合法,并且報(bào)告給.net用戶(hù)是合法的。.net將會(huì)發(fā)送一個(gè)驗(yàn)證cookie到用戶(hù),隨后的訪(fǎng)問(wèn)中,.net以此cookie為依據(jù),來(lái)執(zhí)行授權(quán)的操作。
    例如我們?cè)趌ogin.aspx界面中放置兩個(gè)接受輸入的文本框txtusername和txtpassword,在數(shù)據(jù)庫(kù)中,保存了用戶(hù)名username和密碼userpassword,使用btnlogin按鈕的click事件來(lái)驗(yàn)證用戶(hù):
private void btnlogin_click(object sender, eventargs e)
{
  string sql = "select userid from users where username = '" + txtusername.text.replace("'","_") + "' and userpassword = '" + system.web.security.formsauthentication.hashpasswordforstoringinconfigfile(txtpassword.text, "md5") + "'";
  //使用上面類(lèi)似的sql語(yǔ)句向數(shù)據(jù)庫(kù)執(zhí)行查詢(xún),如果用戶(hù)是合法的,將會(huì)返回?cái)?shù)據(jù)。
  if (...) //根據(jù)條件判定用戶(hù)是合法的
  {
    //下面的語(yǔ)句告訴.net發(fā)送一個(gè)驗(yàn)證cookie給用戶(hù):
    system.web.security.formsauthentication.setauthcookie(userid, false)
    response.redirect("afterlogin.aspx");  //定位到登陸后頁(yè)面
  }
  else
  {
     //用戶(hù)不合法,提示錯(cuò)誤信息
  }
}
以上代碼中,
txtusername.text.replace("'","_")將用戶(hù)輸入的文本中單引號(hào)替換為下劃線(xiàn),以防止sql注入攻擊。
system.web.security.formsauthentication.hashpasswordforstoringinconfigfile(txtpassword.text, "md5")方法將txtpassword.text轉(zhuǎn)換為md5散列值,注意,在用戶(hù)注冊(cè)的時(shí)候,同樣使用此方法將其輸入的注冊(cè)密碼轉(zhuǎn)換為散列值存儲(chǔ)在數(shù)據(jù)庫(kù)中,這里將用戶(hù)輸入的散列值進(jìn)行對(duì)比以決定是否合法用戶(hù)。任何時(shí)候不要將敏感的文本信息以明文方式存放在數(shù)據(jù)庫(kù)中。通過(guò)md5加密,即便此密文被截獲,攻擊者仍無(wú)法獲得真實(shí)的密碼。
當(dāng)確認(rèn)用戶(hù)驗(yàn)證是合法的,則調(diào)用system.web.security.formsauthentication.setauthcookie(userid, false)方法,發(fā)送驗(yàn)證cookie,此方法傳遞兩個(gè)參數(shù),一個(gè)是代表用戶(hù)的標(biāo)示,一般來(lái)說(shuō),在接下來(lái)確認(rèn)用戶(hù)唯一身份的就是從數(shù)據(jù)庫(kù)中獲得的userid。第二個(gè)參數(shù)告訴.net是否寫(xiě)入持續(xù)的cookie,如果為true,則cookie將被持續(xù),下次用戶(hù)再次訪(fǎng)問(wèn)時(shí),cookie仍存在(相當(dāng)于記住用戶(hù),可以提供這樣的復(fù)選框讓用戶(hù)來(lái)決定是否持續(xù)cookie)。發(fā)送了cookie后,即可調(diào)用跳轉(zhuǎn)語(yǔ)句跳轉(zhuǎn)到指定地方。
另外還有一個(gè)方法:web.security.formsauthentication.redirectfromloginpage(string username, bool);將發(fā)送cookie,并且根據(jù)傳遞的returnurl參數(shù)來(lái)跳轉(zhuǎn)到指定的頁(yè)面(相當(dāng)于將上面的兩個(gè)步驟合為一步)。因此login.aspx隱含可以傳遞returnurl,如果沒(méi)有這個(gè)參數(shù),這個(gè)方法將用戶(hù)跳轉(zhuǎn)到default.aspx頁(yè)。
3、授權(quán)資源的訪(fǎng)問(wèn)
    一旦驗(yàn)證了用戶(hù)合法,接下來(lái)要做的事就是對(duì)于用戶(hù)請(qǐng)求的資源,授權(quán)他們是否能夠訪(fǎng)問(wèn)。重新回到web.config文件中,在網(wǎng)站的任何目錄中都可以使用web.config,他們的設(shè)置是傳遞繼承的。
    例如在users目錄中存放的均是當(dāng)用戶(hù)登錄后才能訪(fǎng)問(wèn)的頁(yè)面,則在這個(gè)目錄中創(chuàng)建一個(gè)web.config文件,內(nèi)容如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <!--  授權(quán) 
           此節(jié)設(shè)置應(yīng)用程序的授權(quán)策略。可以允許或拒絕不同的用戶(hù)或角色訪(fǎng)問(wèn)
          應(yīng)用程序資源。通配符: "*" 表示任何人,"?" 表示匿名
          (未經(jīng)身份驗(yàn)證的)用戶(hù)。
    -->
    <authorization>
        <deny users="?" />
    </authorization>
  </system.web>
</configuration>
上述內(nèi)容中deny users="?" 將告訴.net,此目錄拒絕匿名用戶(hù)的訪(fǎng)問(wèn),也就是沒(méi)有驗(yàn)證的用戶(hù)。當(dāng)用戶(hù)試圖請(qǐng)求此目錄中的資源,將會(huì)被重新定向到login.aspx頁(yè)面,要求登陸。沒(méi)有登陸的情況下是無(wú)法訪(fǎng)問(wèn)的。
上述僅對(duì)目錄進(jìn)行定義,程序員不用在頁(yè)面上添加任何代碼,即可完整地實(shí)現(xiàn)了授權(quán)方案。
當(dāng)然,這種僅針對(duì)目錄的授權(quán)配置可能有時(shí)候又會(huì)缺乏靈活,因此,.net也提供location配置節(jié),可以對(duì)指定的資源定義授權(quán):
<configuration>
  <location path="userabc.aspx">
    <system.web>
      <authorization>
        <allow users="a,b,c" />
      </authorization>
    </system.web>
  </location>
</configuration>
其中path是資源相對(duì)路徑。
如果這還不夠靈活的話(huà),.net也提供了在代碼中使用的方法,asp.net頁(yè)全局隱含了一個(gè)只讀的user對(duì)象,通過(guò)獲取user.identity.isauthenticated屬性,可探知用戶(hù)是否驗(yàn)證(即是否登陸),user.identity.name屬性可以獲得用戶(hù)的name,即在驗(yàn)證時(shí)的setauthcookie方法中傳遞的userid。
4、基于角色的授權(quán)
    上面我們講述的用戶(hù)驗(yàn)證,只可能有兩種情況,要么用戶(hù)通過(guò)驗(yàn)證,可以授權(quán)訪(fǎng)問(wèn)資源,要么用戶(hù)沒(méi)有通過(guò)驗(yàn)證,不能訪(fǎng)問(wèn)需要授權(quán)的資源。但是即便是驗(yàn)證通過(guò)的用戶(hù),可能他們所持用的權(quán)限還需要再進(jìn)一步區(qū)分。例如普通用戶(hù)和管理員同樣是需要驗(yàn)證通過(guò)的,但是普通用戶(hù)顯然不能夠訪(fǎng)問(wèn)管理頁(yè)面,而管理員可以。面對(duì)這種情況,.net可以使用基于角色的授權(quán)模型。
    其基本原理是,一旦用戶(hù)驗(yàn)證合法,他們就被分配角色,用戶(hù)可以使一個(gè)或者若干和角色,而資源的授權(quán)面向角色,這樣,針對(duì)不同的角色,就可以授予不同的權(quán)限,沒(méi)有某種角色類(lèi)型的用戶(hù)試圖訪(fǎng)問(wèn)需要這種角色的資源將會(huì)被拒絕。
    當(dāng)網(wǎng)站開(kāi)始接受用戶(hù)請(qǐng)求時(shí),就伴隨著驗(yàn)證,將激發(fā)application_authenticaterequest事件,在global.asax文件中寫(xiě)代碼以響應(yīng)此事件。角色的分配工作就需要再這里進(jìn)行。
public void application_authenticaterequest(object sender, eventargs e)
{
      if (this.request.isauthenticated)
      {
            //這里簡(jiǎn)化了操作,可以從數(shù)據(jù)庫(kù)中獲得角色信息用以構(gòu)造rolesstrarr數(shù)組。作為示例,我們?yōu)槌薬之外的用戶(hù)分配了管理員角色
            string[] rolesstrarr;
            if (this.context.user.identity.name == "a")
            {
                  rolesstrarr = new string[]{"普通用戶(hù)"};
            }
            else
            {
                  rolesstrarr = new string[]{"普通用戶(hù)","管理員"};
            }
            this.context.user = new system.security.principal.genericprincipal(this.user.identity, rolesstrarr);
      }
}
以上代碼清晰明了,因此不再贅述。雖然在全局性有user對(duì)象,但是只有context上下文中的user對(duì)象是可以寫(xiě)入的,我們調(diào)用system.security.principal.genericprincipal方法,在原有user對(duì)象的基礎(chǔ)上為其加入角色。角色列表示一個(gè)字符串?dāng)?shù)組。
    一旦用戶(hù)被授予訪(fǎng)問(wèn)角色之后,在web.config中就可以配置針對(duì)不同角色的訪(fǎng)問(wèn)。例如在管理員admin目錄內(nèi)
<configuration>
  <location path="userabc.aspx">
    <system.web>
      <authorization>
        <allow roles="管理員" />
        <deny users="*" />
      </authorization>
    </system.web>
  </location>
</configuration>
上述配置只允許管理員角色才能被授權(quán)。資源默認(rèn)是任何人都訪(fǎng)問(wèn)的,所以要在下面再添加<deny users="*" />表示對(duì)任何用戶(hù)拒絕。
注意,無(wú)論對(duì)角色或者對(duì)用戶(hù)指定資源的訪(fǎng)問(wèn),如果對(duì)于多個(gè)角色或者讀個(gè)資源,他們之間使用半角逗號(hào)隔開(kāi)。同樣,也可以使用上面講到的方法,對(duì)指定的資源進(jìn)行配置而不是對(duì)整個(gè)目錄。
全局的user對(duì)象提供了一個(gè)方法isinrole(string rolename)方法用來(lái)在代碼中檢測(cè)用戶(hù)是否擁有某種角色。如果他擁有這種角色,將返回true。
后記
    .net提供了完整的安全方面的解決方案,相對(duì)于asp,這是激動(dòng)人心的一個(gè)新特性。只是很多人可能并不能夠熟練地運(yùn)用,而且最痛心的是,很多書(shū)籍上甚至并沒(méi)有這方面的任何描述,甚至連概念都沒(méi)有。這就讓人很懷疑編者的水平了。
    首先,還是要在不斷的實(shí)踐過(guò)程中去了解和體會(huì).net,其實(shí)最好的老師應(yīng)當(dāng)是msdn,到論壇來(lái)發(fā)帖的用戶(hù),我都盡量建議去查閱msdn的資料,msdn除了教給你怎么寫(xiě)代碼,其實(shí)他教給你的還有非常優(yōu)秀的思想和整體概念。只要學(xué)會(huì)使用,沒(méi)有這些書(shū)也可以。從寫(xiě)第一行代碼到現(xiàn)在,除了一本啟蒙書(shū),其他的資源都是msdn或者網(wǎng)上找的,還有就是在每次做項(xiàng)目中的心得。盡管現(xiàn)在看來(lái),啟蒙書(shū)中也沒(méi)有非常全面地講述這些東西。
新聞熱點(diǎn)
疑難解答
圖片精選