本篇開始介紹如何將【畫面結構】設定與【角色/使用者】設定結合。用以設定【授權】
角色/使用者→畫面:角色/使用者擁有哪些畫面的權限
畫面→角色/使用者:一個畫面,對於眾多角色/使用者的權限設定
緣起
本篇開始介紹如何將【畫面結構】設定與【角色/使用者】設定結合。用以設定【授權】
- 角色/使用者→畫面:角色/使用者擁有哪些畫面的權限
- 畫面→角色/使用者:一個畫面,對於眾多角色/使用者的權限設定
在開始講解本篇之前,如果看官您還沒有讀過以前的三個部分,建議您先看看以前的那三個Part,因為這樣比較能夠知道這篇在講些什麼
- ASP.NET進階權限控管-Part 1 願景:這包含了這篇會用到的一些設定的【規則】
- ASP.NET進階權限控管-Part 2 登入結合資料庫、樹狀結構維護:【畫面結構】的設定
- ASP.NET進階權限控管-Part 3 角色管理與角色使用者維護:【角色/使用者】的設定
在這裡小喵把規則貼在此,方便對照
【瀏覽權限控管規則】:
於是就發展出目前小喵的權限控管規則
規則一:(角色管理)因應不同的職務需求,設定【角色】,可由許多使用者扮演相同的角色
規則二:(角色管理)一個【使用者】可以同時扮演許多不同的【角色】
規則三:(授權角色)設定權限時,依照【角色】的職務特性來設定
規則四:(拒絕優先)相同畫面中,當同時擁有的兩個以上【角色】有設定【拒絕】→以【拒絕】為優先
規則五:(個人最優先)當有個人【使用者】發生規則四狀況被拒絕,但右必須使用時→以【個人設定為最優先】
【查詢、新增、修改、刪除、列印權限】
規則一:先有瀏覽權限→再談這些設定(沒有權限瀏覽,一進畫面就被踢出,當然什麼也沒有)
規則二:所屬角色+使用者自己的設定中,只要其中有一可執行→就可執行
例如小喵分屬RoleA,RoleB
RoleA有查詢、新增、修改的權限
RoleB有查詢、刪除的權限
那麼小喵有的權限就是綜合起來的【新增(A)、修改(A)、刪除(B)】
【特殊角色:SysAdmin】
規則:擁有此角色的使用者,擁有最高的權限,任何畫面、任何功能均可使用
資料表設計
在這裡小喵要記錄【畫面節點】在【角色/使用者】擁有的【拒絕、瀏覽、新增、修改、刪除、查詢、列印】等的權限。並且能夠符合小喵在【權限控管規則】中的方式。因此小喵用以下這樣的資料表來存放權限的設定。
由於要將個人、與個人所屬各角色的權限合併後再來判斷,因此將使用者/角色的權限設定在相同的一個資料表中,【拒絕、瀏覽、查詢、新增、修改、刪除、列印】等權限,使用1代表有,0代表無。那麼只要在取得權限時,透過GROUP BY SUM()就能夠將這些權限合併,當查詢結果>0代表有,等於0代表無。最後只需再判斷Deny是否>0,如果SUM(Deny)的結果>0,由於拒絕優先的規則,那麼該項就是拒絕。最後因為個人為最優先,所以還要在讀取是否有個人設定。如果有,則以個人的設定為主,詳細的權限讀取過程小喵會在後面加以整理。先來看看要怎麼去維護這樣的資料。
授權的維護
授權的維護其實有兩個角度:
在這裡特別的是:
- 一開始當所有沒有勾選時,查詢、新增、修改、刪除、列印都不能夠勾選(Disabled)
- 當勾選了【瀏覽】後,才能夠勾選後面的【查詢、新增、修改、刪除、列印】;當【瀏覽取消勾選】,後面項目都變成取消勾選,並且Disabled
- 當【拒絕】被勾選時,後面的所有項目全部【取消勾選並Disabled】,當拒絕【取消勾選】時則是後面的瀏覽回復為Enabled,其他的還是Disabled。
為了能夠符合上面的動作,希望這些自動的設定能夠用Client端的Script來處理。因此小喵捨棄了使用GridView的物件,改以Table物件配合JavaScript來實現他。這部分的程式碼稍微多了一點點,請參考以下用【畫面→使用者】當作範例。
PageLoad的時候
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Me.lblErrMsg.Text = ""
GenJScript()
If Not IsPostBack Then
GenTreeSource()
End If
End Sub
先註冊好畫面會用到的JavaScript
Private Sub GenJScript()
Dim Scrp As String = ""
Scrp += "<Script Language=JavaScript>"
Scrp += "function ChkClick(RUId,ChkType,ChkItm){"
Scrp += " var ChkB=window.document.getElementById('ChkB' + RUId);"
Scrp += " var ChkQ=window.document.getElementById('ChkQ' + RUId);"
Scrp += " var ChkA=window.document.getElementById('ChkA' + RUId);"
Scrp += " var ChkE=window.document.getElementById('ChkE' + RUId);"
Scrp += " var ChkD=window.document.getElementById('ChkD' + RUId);"
Scrp += " var ChkP=window.document.getElementById('ChkP' + RUId);"
Scrp += " var ChkDl=window.document.getElementById('ChkDl' + RUId);"
Scrp += " "
Scrp += " if(ChkType=='N'){" '拒絕
Scrp += " if(ChkItm.checked){"
'設定Checked取消
Scrp += " ChkB.checked=false;"
Scrp += " ChkQ.checked=false;"
Scrp += " ChkA.checked=false;"
Scrp += " ChkE.checked=false;"
Scrp += " ChkD.checked=false;"
Scrp += " ChkP.checked=false;"
Scrp += " ChkDl.checked=false;"
'設定不可點選
Scrp += " ChkB.disabled=true;"
Scrp += " ChkQ.disabled=true;"
Scrp += " ChkA.disabled=true;"
Scrp += " ChkE.disabled=true;"
Scrp += " ChkD.disabled=true;"
Scrp += " ChkP.disabled=true;"
Scrp += " ChkDl.disabled=true;"
Scrp += " }"
Scrp += " else"
Scrp += " {"
'設定可以點選
Scrp += " ChkB.disabled=false;"
Scrp += " }"
Scrp += " }"
Scrp += " if(ChkType=='B')" '瀏覽
Scrp += " {"
Scrp += " if(!ChkItm.checked){"
'設定Checked取消
Scrp += " ChkQ.checked=false;"
Scrp += " ChkA.checked=false;"
Scrp += " ChkE.checked=false;"
Scrp += " ChkD.checked=false;"
Scrp += " ChkP.checked=false;"
Scrp += " ChkDl.checked=false;"
'設定不可點選
Scrp += " ChkQ.disabled=true;"
Scrp += " ChkA.disabled=true;"
Scrp += " ChkE.disabled=true;"
Scrp += " ChkD.disabled=true;"
Scrp += " ChkP.disabled=true;"
Scrp += " ChkDl.disabled=true;"
Scrp += " }"
Scrp += " else"
Scrp += " {"
'設定可以點選
Scrp += " ChkQ.disabled=false;"
Scrp += " ChkA.disabled=false;"
Scrp += " ChkE.disabled=false;"
Scrp += " ChkD.disabled=false;"
Scrp += " ChkP.disabled=false;"
Scrp += " ChkDl.disabled=false;"
Scrp += " }"
Scrp += " }"
Scrp += "}"
Scrp += ""
Scrp += "function ChkAll(ChkType){"
Scrp += " var ItmCnt=window.document.forms(0).hidRUId.length;"
Scrp += " var tmpRUId;"
Scrp += " var tmpChk;"
Scrp += " var tmpChkB;"
Scrp += " var tmpChkN;"
'Scrp += " window.alert(ChkType + ',' + window.event.srcElement.checked + ',' + ItmCnt);"
Scrp += " for(var i=0;i<ItmCnt;i++)"
Scrp += " {"
Scrp += " tmpRUId=window.document.forms(0).hidRUId[i].value;"
Scrp += " tmpChk=window.document.getElementById('Chk'+ChkType+tmpRUId);"
Scrp += " tmpChkB=window.document.getElementById('ChkB'+tmpRUId);"
Scrp += " tmpChkN=window.document.getElementById('ChkN'+tmpRUId);"
Scrp += " if(ChkType=='B'||ChkType=='N')"
Scrp += " {"
Scrp += " if(!tmpChkN.checked)"
Scrp += " {"
Scrp += " tmpChk.checked=window.event.srcElement.checked;"
Scrp += " ChkClick(tmpRUId,ChkType,tmpChk);"
Scrp += " }"
Scrp += " }"
Scrp += " else"
Scrp += " {"
Scrp += " if(tmpChkB.checked==true && tmpChkN.checked==false)"
Scrp += " tmpChk.checked=window.event.srcElement.checked;"
Scrp += " }"
Scrp += " }"
Scrp += "}"
Scrp += "</Script>"
Me.ClientScript.RegisterClientScriptBlock(Me.Page.GetType, "", Scrp)
End Sub
產生Tree
Sub GenTreeSource()
'宣告元件物件
Dim obj As Object
'建立元件實體
obj = CreateObject("PCATMenu.CCATMenu1")
Dim Dt As New DataTable
Dim rc As String
Try
Dim P As String = ""
Dim R As String = ""
Dim UserId As String = ""
'呼叫元件,傳回DataTable
rc = obj.GetAllMenuTree(Dt, P, R, UserId)
If rc = "Success" Then
'宣告TreeView控制項並實體化
Dim Tree1 As TreeView = Me.TreeView1
'宣告TreeNode物件並實體化
Dim tmpNode As New TreeNode
'建立根節點
tmpNode.Text = "首頁"
tmpNode.Value = "首頁"
tmpNode.NavigateUrl = "Default.aspx"
tmpNode.SelectAction = TreeNodeSelectAction.None
Tree1.Nodes.Add(tmpNode)
Dim rcTree As String
'呼叫遞迴建立樹狀結構節點
rcTree = AddNodes(Dt, tmpNode, 0)
If rcTree = "Success" Then
'設定Tree樣式
Tree1.ImageSet = TreeViewImageSet.XPFileExplorer
Tree1.ShowLines = True
'
End If
End If
Catch ex As Exception
'顯示錯誤訊息
lblErrMsg.Text = ex.Message.ToString
Finally
Dt.Dispose()
Dt = Nothing
obj.Dispose()
obj = Nothing
End Try
End Sub
Function AddNodes(ByRef Dt As DataTable, ByRef tNode As TreeNode, ByVal PId As Integer) As String
'******** 遞迴增加樹結構節點 ********
Try
'定義DataRow承接DataTable篩選的結果
Dim rows() As DataRow
'定義篩選的條件
Dim filterExpr As String
filterExpr = "ParentId = " & PId
'資料篩選並把結果傳入Rows
rows = Dt.Select(filterExpr, "Sort")
'如果篩選結果有資料
If rows.GetUpperBound(0) >= 0 Then
Dim row As DataRow
Dim tmpNodeId As Long
Dim tmpsText As String
Dim tmpsValue As String
Dim tmpsUrl As String
Dim tmpsTarget As String
Dim NewNode As TreeNode
Dim rc As String
'逐筆取出篩選後資料
Dim tt As String = ""
For Each row In rows
'放入相關變數中
tmpNodeId = row("NodeId")
tmpsText = row("sText") + "(" + row("NodeId").ToString + ")"
tmpsValue = row("sValue")
tmpsTarget = row("sTarget")
tmpsUrl = row("sURL")
'實體化新節點
NewNode = New TreeNode
'設定節點各屬性
NewNode.Text = tmpsText
NewNode.Value = tmpNodeId
'將節點加入Tree中
tNode.ChildNodes.Add(NewNode)
'呼叫遞回取得子節點
rc = AddNodes(Dt, NewNode, tmpNodeId)
Next
End If
'傳回成功訊息
AddNodes = "Success"
Catch ex As Exception
lblErrMsg.Text = ex.Message
AddNodes = "False"
End Try
End Function
然後在某一結點點選後,顯示角色或者使用者
Sub GenTable()
Dim NodeId As Integer = Me.TreeView1.SelectedNode.Value
Dim obj As Object = CreateObject("PCATMenu.CCATMenu1")
Dim RUCode As String = Me.ddlRUCode.SelectedValue.ToString
Dim P As String = ""
Dim R As String = ""
Try
Dim dt As DataTable = obj.GetNodeRUP(NodeId, RUCode, P, R)
If dt.Rows.Count > 0 Then
Dim tmpRUId As String '角色/使用者代號
Dim tmpRUDesc As String '角色/使用者說明
Dim tmpRUCode As String '角色/使用者區分
Dim tmpPN As String '拒絕
Dim tmpPB As String '瀏覽
Dim tmpPQ As String '查詢
Dim tmpPA As String '新增
Dim tmpPE As String '修改
Dim tmpPD As String '刪除
Dim tmpPP As String '列印
Dim tmpPDl As String '下載
Dim tTmp As String = "" '暫存字串
Dim JScrp As String = ""
Dim y As Integer
For y = 0 To dt.Rows.Count - 1
tmpRUId = dt.Rows(y)("RUId")
tmpRUDesc = dt.Rows(y)("RUDesc")
tmpRUCode = dt.Rows(y)("RUCode")
tmpPN = dt.Rows(y)("PN")
tmpPB = dt.Rows(y)("PB")
tmpPQ = dt.Rows(y)("PQ")
tmpPA = dt.Rows(y)("PA")
tmpPE = dt.Rows(y)("PE")
tmpPD = dt.Rows(y)("PD")
tmpPP = dt.Rows(y)("PP")
tmpPDl = dt.Rows(y)("PDl")
'處理Table
Dim tRow As New TableRow
Dim tCell As TableCell
'設定R/U
tCell = New TableCell
tTmp = tmpRUId + "(" + tmpRUDesc + ")"
tTmp += " <input type=hidden name=hidRUId value=" + tmpRUId.ToString + "> "
tTmp += " <input type=hidden name=hidRUCode value=" + tmpRUCode.ToString + "> "
tCell.Text = tTmp
tCell.CssClass = "tblTreeP01"
tCell.HorizontalAlign = HorizontalAlign.Left
tRow.Cells.Add(tCell)
'設定(Deny)拒絕CheckBox(當拒絕選取→瀏覽、查詢、新增、修改、刪除一並取消,並設定為Disabled)
tCell = New TableCell
tTmp = ""
tTmp += " <input type=checkbox value=1 id=ChkN" + tmpRUId.ToString + " name=ChkN" + tmpRUId.ToString + " "
JScrp = ""
JScrp += " ChkClick('" + tmpRUId.ToString + "','N',this); "
tTmp += " onclick="" " + JScrp + " "" "
If tmpPN = "1" Then
tTmp += " checked "
End If
tTmp += "> "
tTmp += " 拒絕 "
tCell.Text = tTmp
tCell.CssClass = "tblTreeP02"
tRow.Cells.Add(tCell)
'設定瀏覽CheckBox(當瀏覽取消→查詢、新增、修改、刪除一並取消,並設定為Disabled)
tCell = New TableCell
tTmp = ""
JScrp = ""
tTmp += " <input type=checkbox value=1 id=ChkB" + tmpRUId.ToString + " name=ChkB" + tmpRUId.ToString + " "
JScrp += " ChkClick('" + tmpRUId.ToString + "','B',this); "
tTmp += " onclick="" " + JScrp + " "" "
If tmpPB = "1" Then
tTmp += " checked "
End If
tTmp += "> "
tTmp += " 瀏覽 "
tCell.Text = tTmp
tCell.CssClass = "tblTreeP03"
tRow.Cells.Add(tCell)
'設定查詢CheckBox
tCell = New TableCell
tTmp = ""
JScrp = ""
tTmp += " <input type=checkbox value=1 id=ChkQ" + tmpRUId.ToString + " name=ChkQ" + tmpRUId.ToString + " "
If tmpPQ = "1" Then
tTmp += " checked "
End If
If tmpPN = "1" Or tmpPB = "0" Then
tTmp += " disabled "
End If
tTmp += "> "
tTmp += " 查詢 "
tCell.Text = tTmp
tCell.CssClass = "tblTreeP04"
tRow.Cells.Add(tCell)
'設定新增CheckBox
tCell = New TableCell
tTmp = ""
JScrp = ""
tTmp += " <input type=checkbox value=1 id=ChkA" + tmpRUId.ToString + " name=ChkA" + tmpRUId.ToString + " "
If tmpPA = "1" Then
tTmp += " checked "
End If
If tmpPN = "1" Or tmpPB = "0" Then
tTmp += " disabled "
End If
tTmp += "> "
tTmp += " 新增 "
tCell.Text = tTmp
tCell.CssClass = "tblTreeP05"
tRow.Cells.Add(tCell)
'設定修改CheckBox
tCell = New TableCell
tTmp = ""
JScrp = ""
tTmp += " <input type=checkbox value=1 id=ChkE" + tmpRUId.ToString + " name=ChkE" + tmpRUId.ToString + " "
If tmpPE = "1" Then
tTmp += " checked "
End If
If tmpPN = "1" Or tmpPB = "0" Then
tTmp += " disabled "
End If
tTmp += "> "
tTmp += " 修改 "
tCell.Text = tTmp
tCell.CssClass = "tblTreeP06"
tRow.Cells.Add(tCell)
'設定刪除CheckBox
tCell = New TableCell
tTmp = ""
JScrp = ""
tTmp += " <input type=checkbox value=1 id=ChkD" + tmpRUId.ToString + " name=ChkD" + tmpRUId.ToString + " "
If tmpPD = "1" Then
tTmp += " checked "
End If
If tmpPN = "1" Or tmpPB = "0" Then
tTmp += " disabled "
End If
tTmp += "> "
tTmp += " 刪除 "
tCell.Text = tTmp
tCell.CssClass = "tblTreeP07"
tRow.Cells.Add(tCell)
'設定列印CheckBox
tCell = New TableCell
tTmp = ""
JScrp = ""
tTmp += " <input type=checkbox value=1 id=ChkP" + tmpRUId.ToString + " name=ChkP" + tmpRUId.ToString + " "
If tmpPP = "1" Then
tTmp += " checked "
End If
If tmpPN = "1" Or tmpPB = "0" Then
tTmp += " disabled "
End If
tTmp += "> "
tTmp += " 列印 "
tCell.Text = tTmp
tCell.CssClass = "tblTreeP08"
tRow.Cells.Add(tCell)
'設定下載CheckBox
tCell = New TableCell
tTmp = ""
JScrp = ""
tTmp += " <input type=checkbox value=1 id=ChkDl" + tmpRUId.ToString + " name=ChkDl" + tmpRUId.ToString + " "
If tmpPDl = "1" Then
tTmp += " checked "
End If
If tmpPN = "1" Or tmpPB = "0" Then
tTmp += " disabled "
End If
tTmp += "> "
tTmp += " 下載 "
tCell.Text = tTmp
tCell.CssClass = "tblTreeP09"
tRow.Cells.Add(tCell)
Me.tblTree.Rows.Add(tRow)
Next
End If
'Me.GridView1.DataSource = dt
'Me.GridView1.DataBind()
Catch ex As Exception
Me.lblErrMsg.Text = ex.Message.ToString
Finally
obj.Dispose()
obj = Nothing
End Try
End Sub
於是就會顯示如圖的畫面了
接著是如何處理送出後的維護,這邊因為用的是傳統的HTML Tag,所以用Request.Form的方式取得,然後判斷後轉成陣列,呼叫元件進行維護。
Protected Sub btnSave_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSave.Click
Dim RUIdArr() As String = Split(Request.Form("hidRUId"), ",")
Dim RUCodeArr() As String = Split(Request.Form("hidRUCode"), ",")
Dim RUCnt As Integer = UBound(RUIdArr)
If Request.Form("hidRUCode") <> "" Then
Dim UpdMenuP(9, RUCnt) As String
Dim x, y As Integer
Dim tmpRUId As String
Dim tmpRUCode As String
Dim PN As String = ""
Dim PB As String = ""
Dim PQ As String = ""
Dim PA As String = ""
Dim PE As String = ""
Dim PD As String = ""
Dim PP As String = ""
Dim PDl As String = ""
For y = 0 To RUCnt
'Response.Write(RUIdArr(y) & "<br>")
tmpRUId = RUIdArr(y)
tmpRUCode = RUCodeArr(y)
If Not (Request.Form("ChkN" + tmpRUId) = Nothing) Then
PN = Request.Form("ChkN" + tmpRUId)
Else
PN = 0
End If
If Not (Request.Form("ChkB" + tmpRUId) = Nothing) Then
PB = Request.Form("ChkB" + tmpRUId)
Else
PB = 0
End If
If Not (Request.Form("ChkQ" + tmpRUId) = Nothing) Then
PQ = Request.Form("ChkQ" + tmpRUId)
Else
PQ = 0
End If
If Not (Request.Form("ChkA" + tmpRUId) = Nothing) Then
PA = Request.Form("ChkA" + tmpRUId)
Else
PA = 0
End If
If Not (Request.Form("ChkE" + tmpRUId) = Nothing) Then
PE = Request.Form("ChkE" + tmpRUId)
Else
PE = 0
End If
If Not (Request.Form("ChkD" + tmpRUId) = Nothing) Then
PD = Request.Form("ChkD" + tmpRUId)
Else
PD = 0
End If
If Not (Request.Form("ChkP" + tmpRUId) = Nothing) Then
PP = Request.Form("ChkP" + tmpRUId)
Else
PP = 0
End If
If Not (Request.Form("ChkDl" + tmpRUId) = Nothing) Then
PDl = Request.Form("ChkDl" + tmpRUId)
Else
PDl = 0
End If
UpdMenuP(0, y) = tmpRUId
UpdMenuP(1, y) = tmpRUCode
UpdMenuP(2, y) = PN
UpdMenuP(3, y) = PB
UpdMenuP(4, y) = PQ
UpdMenuP(5, y) = PA
UpdMenuP(6, y) = PE
UpdMenuP(7, y) = PD
UpdMenuP(8, y) = PP
UpdMenuP(9, y) = PDl
'For x = 0 To 9
' Response.Write(UpdMenuP(x, y) & ",")
'Next
'Response.Write("<br>")
Next
Dim obj As Object = CreateObject("PCATMenu.CCATMenu2")
Try
Dim NodeId As String = Me.TreeView1.SelectedNode.Value.ToString
Dim RUCodeCtl As String = Me.ddlRUCode.Text
Dim P As String = ""
Dim R As String = ""
Dim rc As String = obj.ChgMenuP2(NodeId, RUCodeCtl, UpdMenuP, P, R, Session("UserId").ToString)
If rc = "Success" Then
Me.lblErrMsg.Text = "維護成功!!"
GenTable()
End If
Catch ex As Exception
Me.lblErrMsg.Text = ex.Message.ToString
Finally
obj.Dispose()
obj = Nothing
End Try
End If
End Sub
程式碼果然是多多
另外有些元件的部分(例如維護資料庫的ChgmenuP2)這類的就省略了,基本上就是把陣列迴圈維護資料庫,比較沒什麼特別的地方。
接著下一篇,將會開始介紹如何用TreeView展現個人有權限(包含個人與所屬角色)的畫面目錄,以及點入每個畫面後,如何去驗證這些權限。
|
|
以下是簽名:
- 歡迎轉貼本站的文章,不過請在貼文主旨上加上【轉貼】,並在文章中附上本篇的超連結與站名【topcat姍舞之間的極度凝聚】,感恩大家的配合。
- 小喵大部分的文章會以小喵熟悉的語言VB.NET撰寫,如果您需要C#的Code,也許您可以試著用線上的工具進行轉換,這裡提供幾個參考
Microsoft MVP Visual Studio and Development Technologies (2005~2019/6) | topcat Blog:http://www.dotblogs.com.tw/topcat |