基于用戶的安全策略在B/S中的完成(2)
發表時間:2024-01-29 來源:明輝站整理相關軟件相關文章人氣:
[摘要]2 菜單管理 菜單管理主要包括兩部分的內容,菜單項的編輯維護與根據當前登陸用戶的權限進行菜單的生成。 菜單的編輯維護就是指對菜單表的維護。因為比較簡單,在此不做詳細說明,只是要注意以下三個問題: 第一、菜單編號與權限位數的生成。因為菜單項是可以刪除的,如果新增菜單項獲取編號與權限位數時只是在最大的...
2 菜單管理
菜單管理主要包括兩部分的內容,菜單項的編輯維護與根據當前登陸用戶的權限進行菜單的生成。
菜單的編輯維護就是指對菜單表的維護。因為比較簡單,在此不做詳細說明,只是要注意以下三個問題:
第一、菜單編號與權限位數的生成。因為菜單項是可以刪除的,如果新增菜單項獲取編號與權限位數時只是在最大的編號上增1的話,必須會導致編碼資源的浪費,特別是權限位數,只有100位,如果由于跳號而導致了編碼資源的浪費,必然導致新功能模塊無編碼資源可用。所以,在添加新菜單項是必須要解決跳號的問題。請看以下取編號與權限位數的語句:
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)
我們只要保證對于菜單維護本身的這個功能模塊給它分配菜單編號為1,權限位數為1的話,就能夠保證在任何情況下新增菜單項時菜單編號與權限位數不會跳號,編碼資源能夠得到充分利用。因為菜單維護本身這個菜單項在通常情況下是不會被刪除的。
第二、入口路徑是指進入本功能模塊的第一個頁面的連接地址,請注意路徑的相對性問題。
第三、相關文件是指完成本功能模塊的頁面組合。一般情況下,一個菜單項下連接一個功能模塊,要完成該功能模塊的功能可能需要若干個ASP頁面。例如,菜單項1的入口路徑是../menu1.asp,還要以下三個頁面,menu11.asp, menu12.asp, menu13.asp,則相關文件的內容是指meunu1.asp, menu11.asp,menu12.asp,menu13.asp這個字符串序例。該項的主要作用是為了安全檢查而設置的,也就是頁面在系統中的注冊過程。
根據當前登陸用戶的權限進行菜單生成時有一個關鍵的會話變量:Session("YHQX"),該變量在后面將要進行描述的安全檢查過程中生成,一直保存到用戶退出系統或者會話超時失效時為止。里面的內容就是100位的當前登陸用戶的權限字符串。根據該變量生成菜單的過程如下:
<% 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)
'獲取當前父菜單下的所有子菜單
'遍歷當前父菜單下的子菜單
do while not Rs1.eof
if Mid(Session("YHQX"),Rs1("QXWS"),1)=1 then
SubMenuNum=SubMenuNum+1
'如果當前用戶有當前子菜單的權限,則子菜單的個數加上1
end if
Rs1.movenext
loop
Rs1.close
set Rs1=nothing
if SubMenuNum>0 then
'如果當前用戶的當前父菜單的下子菜單個數大于零則生成該父菜單
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 >
'菜單的形式與樣式根據需要自己調整,這里采用泛指方式
<%
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>
'菜單的形式與樣式根據需要自己調整,這里采用泛指方式
<%
end if
Rs1.movenext
loop
Rs1.close
set Rs1=nothing
end if
Rs.movenext
loop
Rs.close
set Rs=nothing
%>
由于本系統只有兩級菜單,所以沒有牽涉到菜單遞歸調用生成的問題。如果要實現的話必須把菜單生成過程定義成函數形式,才能進行遞歸調用,在此不做詳細討論。
3 權限分配
權限的分配由用戶組權限分配與用戶的權限分配兩部分組成。是將具體的菜單與用戶組或者用戶關聯的過程。
首先看用戶組的添加界面如下:
<form name="zjyhz" method="post" action="yhzbmb_zj.asp" onsubmit="return checkinput(this)"> <!-- 調用輸入檢驗函數,并生成權限字符串-->
<input type="hidden" name="action" value="zjyhz">
<table cellpadding="0" cellspacing="0" width="100%">
<tr>
<td><fieldset align="center"><legend>增加用戶組
</legend>
<input type="hidden" name="DQDWDM" value="<%=DQDWDM%>"><!--由前一頁傳入,當前單位編碼-->
用戶組名稱:<input type="text" name="yhzmc" size="50" maxlength="50" ><br><br>
設置用戶組權限:<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
'但是只有當前用戶擁有的權限它才能將該權限賦給它所新建立的用戶組,有利的保證了安全性,只能繼承,不能超越,將滿足條件的菜單項顯示在例表框中,待用戶選擇。
%>
<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="*">
<!—已經選好的菜單項的例表框-->
<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函數實現界面的操作以及輸入驗證工作-->
<script language="javascript">
//在一個例表框中插入一項
function InsertItem(ListObj,ListText,ListValue)
{ len=ListObj.length;
ListObj.options[len] = new Option(ListText,ListValue);}
//將例表框中指定的項刪除
function DeleteItem(ListObj,DeleteIndex)
{ ListObj.options[DeleteIndex] = null;}
//將一項從一個例表框移到另一個例表框
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);}
}
//輸入校驗并生成新用戶組權限字符串
function checkinput(FormHandle)
{ if(FormHandle.yhzmc.value=="")
{ alert("用戶組名稱不能為空!");
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提交后寫入數據庫就完成了用戶組的添加,這里也有用戶組編號可能跳號的問題,可以用上面已經描述過的方法加以解決。當對用戶組的權限進行編輯時,我們必須將屬于當前登陸用戶但不屬于該用戶組的權限顯示在一個例表框中,屬于該用戶組的權限顯示在別一個例表框中,用以下語句分別實現:
<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")就是需要編輯的用戶組的權限,在更上面的語句中進行初始化%>
<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")就是需要編輯的用戶組的權限,在更上面的語句中進行初始化
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>
其它操作過程與添加時基本一致。
用戶的操作過程與用戶組的操作過程基本上也是一樣的,所用到的代碼也大致相同,不過還是有幾個要注意的問題:
第一、添加用戶時不需要指定用戶的權限,只要指定該用戶所屬的用戶組即可,用戶權限自動從用戶組繼承。當然添加完了也可以對用戶組的權限進行修改,為了保障安全,修改的范圍也只限當前登陸的用戶權限范圍內。實現如上所述相同。
第二、用戶密碼的存貯,建議不要存貯為明碼,請加密存貯,在本系統中采取了不可逆轉的加密算法,即使知道了加密算法與加密后的結果,也不可能還原為原始密碼,有利的保障了安全性。
第三、每當在用戶組中添加一個用戶時用戶組表的用戶個數就會增加1,當然刪除一個用戶也會使用該組的用戶個數減1。
第四、用戶所屬的用戶組可以修改,修改后用戶的權限與新屬用戶組的權限一樣。
4 安全檢查
安全檢查指登陸的安全檢查與訪問每個頁面時的安全檢查兩個方面。
登陸時的安全檢查指驗證用戶名與密碼,如果驗證通過,將該用戶的用戶權限存貯于會話變量Session("YHQX")中,以利于隨時檢驗。然后引導入主界面,并根據Session("YHQX")生成系統菜單。
我們重點說一下訪問每個頁面時的安全檢查。大家都知道,與C/S程序不同,某個菜單沒有顯示出來,就并不代表用戶沒有辦法訪問該菜單項的功能,因為用戶可以通過直接輸入URL訪問到該功能模塊,所以必須在訪問每個頁面是也要提供一個權限控制機制,防止用戶越權使用。我們用如下代碼實現了訪問頁面時的權限檢驗:
<% ScriptName=Request.ServerVariables("SCRIPT_NAME")
'取得當次請求的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)
'分離出請求的ASP頁面文件名
sql="select 權限位數 from 菜單權限表 where
相關文件 like '%"&ScriptName&"%'"
set Rs1=Conn.execute(sql)
'查找其屬于哪一位權限,根據上面所述的相關文件進行查找
if Rs1.eof and Rs1.bof then
response.write "非法訪問!"&ScriptName&"還沒有進行注冊,請通知系統管理員進行注冊!"
Rs1.close
set Rs1=nothing
response.end
end if
'比較當前用戶的權限,看是否有訪問當前頁面的權利
Qxbit=Mid(Session("YHQX"),Rs1(0),1)
if Qxbit="" then
response.write "非法訪問!"&"<br>權限位數錯誤,可能是由于您長時間沒有操作而超時退出!<br>請重新登陸或聯系管理員!"
Rs1.close
set Rs1=nothing
response.end
end if
if Qxbit="0" then
response.write "非法訪問!"&"您沒有訪問此頁的權限!"
Rs1.close
set Rs1=nothing
response.end
end if
Rs1.close
set Rs1=nothing
%>
將該文件存為validate.inc
然后在系統中的每個文件的最頂部用如下語句包含進去:
<!--#include file="validate.inc"-->
這樣一來,就實現了頁面級的權限檢查,并且非常方便。甚至,與一些WEB服務器配合,這個執行過程可以自動完成,并不需要在系統的每一個文件頂部包含validate.inc文件,WEB服務器在調用每一個頁面前會自動將validate.inc包含到文件頂部。
在需要開發基于用戶的安全策略的B/S應用時,首先將以上所述的框架搭建起來,然后再單獨開發各個功能模塊,然后用提供的菜單管理功能,將功能模塊以搭積木的方式添加進來,這樣非常方便,也利于團隊的協作開發。由于有開放的菜單與統一的權限控制策略,所開發的應用也能夠很好的與用戶本身所擁有的B/S應用或者其它廠商的B/S應用集成在一起,為用戶提供統一的用戶操作界面與權限控制體系,這將使產品非常有竟爭力。在我們開發的稅務系統后臺管理系統中,利用此框架,實現了與用戶方和第三方廠商應用的集成。