基于用戶(hù)的安全策略在B/S中的完成(2)
發(fā)表時(shí)間:2024-06-01 來(lái)源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]2 菜單管理 菜單管理主要包括兩部分的內(nèi)容,菜單項(xiàng)的編輯維護(hù)與根據(jù)當(dāng)前登陸用戶(hù)的權(quán)限進(jìn)行菜單的生成。 菜單的編輯維護(hù)就是指對(duì)菜單表的維護(hù)。因?yàn)楸容^簡(jiǎn)單,在此不做詳細(xì)說(shuō)明,只是要注意以下三個(gè)問(wèn)題: 第一、菜單編號(hào)與權(quán)限位數(shù)的生成。因?yàn)椴藛雾?xiàng)是可以刪除的,如果新增菜單項(xiàng)獲取編號(hào)與權(quán)限位數(shù)時(shí)只是在最大的...
2 菜單管理
菜單管理主要包括兩部分的內(nèi)容,菜單項(xiàng)的編輯維護(hù)與根據(jù)當(dāng)前登陸用戶(hù)的權(quán)限進(jìn)行菜單的生成。
菜單的編輯維護(hù)就是指對(duì)菜單表的維護(hù)。因?yàn)楸容^簡(jiǎn)單,在此不做詳細(xì)說(shuō)明,只是要注意以下三個(gè)問(wèn)題:
第一、菜單編號(hào)與權(quán)限位數(shù)的生成。因?yàn)椴藛雾?xiàng)是可以刪除的,如果新增菜單項(xiàng)獲取編號(hào)與權(quán)限位數(shù)時(shí)只是在最大的編號(hào)上增1的話(huà),必須會(huì)導(dǎo)致編碼資源的浪費(fèi),特別是權(quán)限位數(shù),只有100位,如果由于跳號(hào)而導(dǎo)致了編碼資源的浪費(fèi),必然導(dǎo)致新功能模塊無(wú)編碼資源可用。所以,在添加新菜單項(xiàng)是必須要解決跳號(hào)的問(wèn)題。請(qǐng)看以下取編號(hào)與權(quán)限位數(shù)的語(yǔ)句:
select min(CDBH)+1 from CDB where CDBH+1 not in(select CDBH from CDB)
select min(QXWS)+1 from CDB where QXWS+1 not in(select QXWS from CDB)
我們只要保證對(duì)于菜單維護(hù)本身的這個(gè)功能模塊給它分配菜單編號(hào)為1,權(quán)限位數(shù)為1的話(huà),就能夠保證在任何情況下新增菜單項(xiàng)時(shí)菜單編號(hào)與權(quán)限位數(shù)不會(huì)跳號(hào),編碼資源能夠得到充分利用。因?yàn)椴藛尉S護(hù)本身這個(gè)菜單項(xiàng)在通常情況下是不會(huì)被刪除的。
第二、入口路徑是指進(jìn)入本功能模塊的第一個(gè)頁(yè)面的連接地址,請(qǐng)注意路徑的相對(duì)性問(wèn)題。
第三、相關(guān)文件是指完成本功能模塊的頁(yè)面組合。一般情況下,一個(gè)菜單項(xiàng)下連接一個(gè)功能模塊,要完成該功能模塊的功能可能需要若干個(gè)ASP頁(yè)面。例如,菜單項(xiàng)1的入口路徑是../menu1.asp,還要以下三個(gè)頁(yè)面,menu11.asp, menu12.asp, menu13.asp,則相關(guān)文件的內(nèi)容是指meunu1.asp, menu11.asp,menu12.asp,menu13.asp這個(gè)字符串序例。該項(xiàng)的主要作用是為了安全檢查而設(shè)置的,也就是頁(yè)面在系統(tǒng)中的注冊(cè)過(guò)程。
根據(jù)當(dāng)前登陸用戶(hù)的權(quán)限進(jìn)行菜單生成時(shí)有一個(gè)關(guān)鍵的會(huì)話(huà)變量:Session("YHQX"),該變量在后面將要進(jìn)行描述的安全檢查過(guò)程中生成,一直保存到用戶(hù)退出系統(tǒng)或者會(huì)話(huà)超時(shí)失效時(shí)為止。里面的內(nèi)容就是100位的當(dāng)前登陸用戶(hù)的權(quán)限字符串。根據(jù)該變量生成菜單的過(guò)程如下:
<% sql="select * from CDB where FCDBH=0 order by CDBH"
set Rs=Conn.execute(sql)
'取得菜單表中的所有有子菜單的父菜單
'遍歷父菜單
do while not Rs.eof
SubMenuNum=0
sql="select QXWS from CDB where FCDBH="&Rs("CDBH")&" order by CDBH"
set Rs1=Conn.execute(sql)
'獲取當(dāng)前父菜單下的所有子菜單
'遍歷當(dāng)前父菜單下的子菜單
do while not Rs1.eof
if Mid(Session("YHQX"),Rs1("QXWS"),1)=1 then
SubMenuNum=SubMenuNum+1
'如果當(dāng)前用戶(hù)有當(dāng)前子菜單的權(quán)限,則子菜單的個(gè)數(shù)加上1
end if
Rs1.movenext
loop
Rs1.close
set Rs1=nothing
if SubMenuNum>0 then
'如果當(dāng)前用戶(hù)的當(dāng)前父菜單的下子菜單個(gè)數(shù)大于零則生成該父菜單
if Rs.bof then FirstMenu=Rs("CDBH") end if
MenuNum=Rs("CDBH")
ParentName=Rs("CDMC")
response.write "<!-- "&ParentName&"父菜單 -->"
%>
<menu class=parent name=<%=ParentName%> url=<%=Rs("RKLJ")%> target= mainFrame >
'菜單的形式與樣式根據(jù)需要自己調(diào)整,這里采用泛指方式
<%
response.write "<!-- "&ParentName&"子菜單 -->"
sql="select * from CDB where FCDBH="&MenuNum&" order by CDBH"
set Rs1=Conn.execute(sql)
'查找該父菜單下的所有子菜單
do while not Rs1.eof
if Mid(Session("YHQX"),Rs1("YHQX"),1)=1 then
%>
<menu class=child name=<%=Rs("CDMC")%> url=<%=Rs("RKLJ")%> target= mainFrame ><br>
'菜單的形式與樣式根據(jù)需要自己調(diào)整,這里采用泛指方式
<%
end if
Rs1.movenext
loop
Rs1.close
set Rs1=nothing
end if
Rs.movenext
loop
Rs.close
set Rs=nothing
%>
由于本系統(tǒng)只有兩級(jí)菜單,所以沒(méi)有牽涉到菜單遞歸調(diào)用生成的問(wèn)題。如果要實(shí)現(xiàn)的話(huà)必須把菜單生成過(guò)程定義成函數(shù)形式,才能進(jìn)行遞歸調(diào)用,在此不做詳細(xì)討論。
3 權(quán)限分配
權(quán)限的分配由用戶(hù)組權(quán)限分配與用戶(hù)的權(quán)限分配兩部分組成。是將具體的菜單與用戶(hù)組或者用戶(hù)關(guān)聯(lián)的過(guò)程。
首先看用戶(hù)組的添加界面如下:
<form name="zjyhz" method="post" action="yhzbmb_zj.asp" onsubmit="return checkinput(this)"> <!-- 調(diào)用輸入檢驗(yàn)函數(shù),并生成權(quán)限字符串-->
<input type="hidden" name="action" value="zjyhz">
<table cellpadding="0" cellspacing="0" width="100%">
<tr>
<td><fieldset align="center"><legend>增加用戶(hù)組
</legend>
<input type="hidden" name="DQDWDM" value="<%=DQDWDM%>"><!--由前一頁(yè)傳入,當(dāng)前單位編碼-->
用戶(hù)組名稱(chēng):<input type="text" name="yhzmc" size="50" maxlength="50" ><br><br>
設(shè)置用戶(hù)組權(quán)限:<br>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td width="150">
<input type="hidden" name="yhzqx">
<select id="allqx" size=10 style='width:150'>
<%
sql="SELECT CDMC, QXWS FROM CDB where FCDBH<>0 ORDER BY CDBH"
set Rs1=Conn.exece(sql)
'取得所有子菜單
%>
<%do while not Rs1.eof
if Mid(Session("YHQX"),Rs1("QXWS"),1)=1 then
'但是只有當(dāng)前用戶(hù)擁有的權(quán)限它才能將該權(quán)限賦給它所新建立的用戶(hù)組,有利的保證了安全性,只能繼承,不能超越,將滿(mǎn)足條件的菜單項(xiàng)顯示在例表框中,待用戶(hù)選擇。
%>
<option value="<%=Rs1("QXWS")%>"><%=Rs1("CDMC")%></option>
<%
end if
Rs1.movenext
loop
Rs1.close
set Rs1=nothing
%>
</select>
</td>
<td width="30" align="center">
<input type="button" name="Submit3" value=" -> " class="addbutton" onclick="MoveItem(document.zjyhz.allqx,document.zjyhz.selectqx)">
<input type="button" name="Submit32" value=" <- " class="addbutton" onclick="MoveItem(document.zjyhz.selectqx,document.zjyhz.allqx)">
</td>
<td width="*">
<!—已經(jīng)選好的菜單項(xiàng)的例表框-->
<select id="selectqx" size=10 style='width:150'>
</select>
</td>
</tr>
</table><br>
<input type="submit" name="Submit" value="增 加">
<input type="reset" name="Submit2" value="清 空">
<input type="button" name="Submit2" value="放 棄" onclick="location.href='yhzbmb.asp?DQDWDM=<%=DQDWDM%>'">
</fieldset></td>
</tr>
</table>
</form>
<!—以下javascript函數(shù)實(shí)現(xiàn)界面的操作以及輸入驗(yàn)證工作-->
<script language="javascript">
//在一個(gè)例表框中插入一項(xiàng)
function InsertItem(ListObj,ListText,ListValue)
{ len=ListObj.length;
ListObj.options[len] = new Option(ListText,ListValue);}
//將例表框中指定的項(xiàng)刪除
function DeleteItem(ListObj,DeleteIndex)
{ ListObj.options[DeleteIndex] = null;}
//將一項(xiàng)從一個(gè)例表框移到另一個(gè)例表框
function MoveItem(srcListObj,decListObj)
{ if(srcListObj.selectedIndex!=-1)
{ ListValue=srcListObj.options[srcListObj.selectedIndex].value; ListText=srcListObj.options[srcListObj.selectedIndex].text;
DeleteItem(srcListObj,srcListObj.selectedIndex);
for(i=0;i<decListObj.length;i++)
{ if(ListValue==decListObj.options[i].value) return}
InsertItem(decListObj,ListText,ListValue);}
}
//輸入校驗(yàn)并生成新用戶(hù)組權(quán)限字符串
function checkinput(FormHandle)
{ if(FormHandle.yhzmc.value=="")
{ alert("用戶(hù)組名稱(chēng)不能為空!");
FormHandle.yhzmc.focus();
return false; }
var strYhzqx="0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
var beforebit,afterbit
for(i=0;i<FormHandle.selectqx.length;i++)
{ beforebit=strYhzqx.substr(0,FormHandle.selectqx.options[i].value-1);
afterbit=strYhzqx.substr(FormHandle.selectqx.options[i].value,99);
strYhzqx=beforebit+"1"+afterbit; }
if(strYhzqx.length!=100) alert("bit error");
FormHandle.yhzqx.value=strYhzqx;
return true;}
</script>
以上FORM提交后寫(xiě)入數(shù)據(jù)庫(kù)就完成了用戶(hù)組的添加,這里也有用戶(hù)組編號(hào)可能跳號(hào)的問(wèn)題,可以用上面已經(jīng)描述過(guò)的方法加以解決。當(dāng)對(duì)用戶(hù)組的權(quán)限進(jìn)行編輯時(shí),我們必須將屬于當(dāng)前登陸用戶(hù)但不屬于該用戶(hù)組的權(quán)限顯示在一個(gè)例表框中,屬于該用戶(hù)組的權(quán)限顯示在別一個(gè)例表框中,用以下語(yǔ)句分別實(shí)現(xiàn):
<select id="allqx" size=10 style='width:150'>
<%sql="SELECT CDMC, QXWS FROM CDB where FCDBH<>0 ORDER BY CDBH"
set Rs1=Conn.execute(sql) %>
<%do while not Rs1.eof
if Mid(Session("YHQX"),Rs1("QXWS"),1)=1 then
if Mid(Rs("YHZQX"),Rs1("QXWS"),1)=0 then
'Rs("YHZQX")就是需要編輯的用戶(hù)組的權(quán)限,在更上面的語(yǔ)句中進(jìn)行初始化%>
<option value="<%=Rs1("QXWS")%>"><%=Rs1("CDMC")%></option>
<% end if end if Rs1.movenext loop Rs1.close set Rs1=nothing %>
</select>
<select id="selectqx" size=10 style='width:150'>
<%yhzqx=trim(Rs("YHZQX"))
'Rs("YHZQX")就是需要編輯的用戶(hù)組的權(quán)限,在更上面的語(yǔ)句中進(jìn)行初始化
for i=1 to len(yhzqx)
if(mid(yhzqx,i,1)="1") then
sql="select CDMC from CDB where QXWS="&i
set Rs1=Conn.execute(sql)
if Not Rs1.eof then%>
<option value="<%=i%>"><%=Rs1("CDMC")%></option>
<%end if Rs1.close set Rs1=nothing end if next Rs1.close set Rs1=nothing %>
</select>
其它操作過(guò)程與添加時(shí)基本一致。
用戶(hù)的操作過(guò)程與用戶(hù)組的操作過(guò)程基本上也是一樣的,所用到的代碼也大致相同,不過(guò)還是有幾個(gè)要注意的問(wèn)題:
第一、添加用戶(hù)時(shí)不需要指定用戶(hù)的權(quán)限,只要指定該用戶(hù)所屬的用戶(hù)組即可,用戶(hù)權(quán)限自動(dòng)從用戶(hù)組繼承。當(dāng)然添加完了也可以對(duì)用戶(hù)組的權(quán)限進(jìn)行修改,為了保障安全,修改的范圍也只限當(dāng)前登陸的用戶(hù)權(quán)限范圍內(nèi)。實(shí)現(xiàn)如上所述相同。
第二、用戶(hù)密碼的存貯,建議不要存貯為明碼,請(qǐng)加密存貯,在本系統(tǒng)中采取了不可逆轉(zhuǎn)的加密算法,即使知道了加密算法與加密后的結(jié)果,也不可能還原為原始密碼,有利的保障了安全性。
第三、每當(dāng)在用戶(hù)組中添加一個(gè)用戶(hù)時(shí)用戶(hù)組表的用戶(hù)個(gè)數(shù)就會(huì)增加1,當(dāng)然刪除一個(gè)用戶(hù)也會(huì)使用該組的用戶(hù)個(gè)數(shù)減1。
第四、用戶(hù)所屬的用戶(hù)組可以修改,修改后用戶(hù)的權(quán)限與新屬用戶(hù)組的權(quán)限一樣。
4 安全檢查
安全檢查指登陸的安全檢查與訪(fǎng)問(wèn)每個(gè)頁(yè)面時(shí)的安全檢查兩個(gè)方面。
登陸時(shí)的安全檢查指驗(yàn)證用戶(hù)名與密碼,如果驗(yàn)證通過(guò),將該用戶(hù)的用戶(hù)權(quán)限存貯于會(huì)話(huà)變量Session("YHQX")中,以利于隨時(shí)檢驗(yàn)。然后引導(dǎo)入主界面,并根據(jù)Session("YHQX")生成系統(tǒng)菜單。
我們重點(diǎn)說(shuō)一下訪(fǎng)問(wèn)每個(gè)頁(yè)面時(shí)的安全檢查。大家都知道,與C/S程序不同,某個(gè)菜單沒(méi)有顯示出來(lái),就并不代表用戶(hù)沒(méi)有辦法訪(fǎng)問(wèn)該菜單項(xiàng)的功能,因?yàn)橛脩?hù)可以通過(guò)直接輸入U(xiǎn)RL訪(fǎng)問(wèn)到該功能模塊,所以必須在訪(fǎng)問(wèn)每個(gè)頁(yè)面是也要提供一個(gè)權(quán)限控制機(jī)制,防止用戶(hù)越權(quán)使用。我們用如下代碼實(shí)現(xiàn)了訪(fǎng)問(wèn)頁(yè)面時(shí)的權(quán)限檢驗(yàn):
<% ScriptName=Request.ServerVariables("SCRIPT_NAME")
'取得當(dāng)次請(qǐng)求的URL
for i=len(ScriptName) to 1 step -1
if Mid(ScriptName,i,1)="/" then exit for
next
ScriptName=Mid(ScriptName,i+1,len(ScriptName)-i)
'分離出請(qǐng)求的ASP頁(yè)面文件名
sql="select 權(quán)限位數(shù) from 菜單權(quán)限表 where
相關(guān)文件 like '%"&ScriptName&"%'"
set Rs1=Conn.execute(sql)
'查找其屬于哪一位權(quán)限,根據(jù)上面所述的相關(guān)文件進(jìn)行查找
if Rs1.eof and Rs1.bof then
response.write "非法訪(fǎng)問(wèn)!"&ScriptName&"還沒(méi)有進(jìn)行注冊(cè),請(qǐng)通知系統(tǒng)管理員進(jìn)行注冊(cè)!"
Rs1.close
set Rs1=nothing
response.end
end if
'比較當(dāng)前用戶(hù)的權(quán)限,看是否有訪(fǎng)問(wèn)當(dāng)前頁(yè)面的權(quán)利
Qxbit=Mid(Session("YHQX"),Rs1(0),1)
if Qxbit="" then
response.write "非法訪(fǎng)問(wèn)!"&"<br>權(quán)限位數(shù)錯(cuò)誤,可能是由于您長(zhǎng)時(shí)間沒(méi)有操作而超時(shí)退出!<br>請(qǐng)重新登陸或聯(lián)系管理員!"
Rs1.close
set Rs1=nothing
response.end
end if
if Qxbit="0" then
response.write "非法訪(fǎng)問(wèn)!"&"您沒(méi)有訪(fǎng)問(wèn)此頁(yè)的權(quán)限!"
Rs1.close
set Rs1=nothing
response.end
end if
Rs1.close
set Rs1=nothing
%>
將該文件存為validate.inc
然后在系統(tǒng)中的每個(gè)文件的最頂部用如下語(yǔ)句包含進(jìn)去:
<!--#include file="validate.inc"-->
這樣一來(lái),就實(shí)現(xiàn)了頁(yè)面級(jí)的權(quán)限檢查,并且非常方便。甚至,與一些WEB服務(wù)器配合,這個(gè)執(zhí)行過(guò)程可以自動(dòng)完成,并不需要在系統(tǒng)的每一個(gè)文件頂部包含validate.inc文件,WEB服務(wù)器在調(diào)用每一個(gè)頁(yè)面前會(huì)自動(dòng)將validate.inc包含到文件頂部。
在需要開(kāi)發(fā)基于用戶(hù)的安全策略的B/S應(yīng)用時(shí),首先將以上所述的框架搭建起來(lái),然后再單獨(dú)開(kāi)發(fā)各個(gè)功能模塊,然后用提供的菜單管理功能,將功能模塊以搭積木的方式添加進(jìn)來(lái),這樣非常方便,也利于團(tuán)隊(duì)的協(xié)作開(kāi)發(fā)。由于有開(kāi)放的菜單與統(tǒng)一的權(quán)限控制策略,所開(kāi)發(fā)的應(yīng)用也能夠很好的與用戶(hù)本身所擁有的B/S應(yīng)用或者其它廠(chǎng)商的B/S應(yīng)用集成在一起,為用戶(hù)提供統(tǒng)一的用戶(hù)操作界面與權(quán)限控制體系,這將使產(chǎn)品非常有竟?fàn)幜ΑT谖覀冮_(kāi)發(fā)的稅務(wù)系統(tǒng)后臺(tái)管理系統(tǒng)中,利用此框架,實(shí)現(xiàn)了與用戶(hù)方和第三方廠(chǎng)商應(yīng)用的集成。