感謝大家最近對本系列的關(guān)注和評論,我會繼續(xù)完善內(nèi)容,并且總結(jié)教訓(xùn)寫出更好的東東來。
今天談?wù)勛罱谘芯康腞CP安全模型,其實RCP在誕生之初就是建立在一個非常魯棒的框架之上的---OSGi,它不但有全新的概念,全新的思路,全新的熱插拔技術(shù),還有非常好的安全模型(
equinox security 項目好像還在孵化中) 。
解決RCP的安全問題,從最近的調(diào)研中看是有四種方式:
1.equinox security項目;
2.使用org.eclipse.ui.activities擴(kuò)展點定義可訪問權(quán)限和角色;
3.使用Eclipse-JAAS插件;
4.自己寫代碼在對功能點進(jìn)行過濾;
第一種方案不可行,因為equinox security項目還在孵化中,而且沒有充足的文檔,因此放棄;
第二種方案可行,但是靈活性不夠,我們很難在項目開始之初充分的定義出所有的角色以及角色對應(yīng)的權(quán)限,而且還要引入大量的配置,所以也放棄;
第三種方案可能可行,因為從老外的文檔中看出,這個插件是可用的,而且是強(qiáng)大的,它的原理就是定義了一堆自己的擴(kuò)展點,然后通過分析擴(kuò)展點,過濾掉不可用的功能點。但是這個插件的更新時間竟然是2005年,N久都沒有人管過了~而且還是基于Eclipse3.0的實現(xiàn),所以也放棄了;
我選擇的是第四種方案,自己寫代碼對功能點進(jìn)行過濾,其實思路很簡單,我們定義基類,在基類中過濾掉有那些功能點可以被顯示,或者不能被顯示。
RCP應(yīng)用中所需要關(guān)注安全點主要是:
1.Action
2.Viewer
3.編輯器
4.透視圖
經(jīng)過討論,其實我們的關(guān)注點主要集中在Action和透視圖,因為透視圖總會包含一組Viewer出現(xiàn)所以我們只要在透視圖布局類中將我們不需要關(guān)注的Viewer過濾掉,那么Viewer就可以被管理起來了,而編輯器的出現(xiàn)也主要是針對Viewer出現(xiàn)的,如果一個Viewer不能顯示,那么和它對應(yīng)的編輯器也永遠(yuǎn)無法顯示,所以編輯器幾乎不用考慮。
其實安全模型的核心就是Action,Viewer,透視圖。
對于Action,存在兩種,一種是開發(fā)人員自己在代碼中創(chuàng)建出來的Action(new Action),另一種就是在plugin.xml中配置的。
第一種情況的基類:
1
package com.glnpu.dmp.client.platform.core.internal.security.absaction;
2
3
import org.apache.log4j.Logger;
4
import org.eclipse.jface.action.Action;
5
import org.eclipse.jface.resource.ImageDescriptor;
6
7
import com.glnpu.dmp.client.platform.core.internal.security.SecurityManager;
8
9
/** *//**
10
* 繼承自<code>Action</code>,在構(gòu)造方法中加入用戶權(quán)限判斷。
11
* @author lign
12
*
13
*/
14
public abstract class AbstractSecurityAction extends Action
{
15
private Logger log = Logger.getLogger(this.getClass());
16
17
public AbstractSecurityAction(String text, ImageDescriptor image)
{
18
super(text, image);
19
this.setActionId();
20
log.debug("當(dāng)前處理的Action為: " + this.getId());
21
//權(quán)限判斷
22
SecurityManager.securityFiltration(this);
23
}
24
/** *//**
25
* 實現(xiàn)者必須實現(xiàn)此方法,并在方法內(nèi)調(diào)用setId方法,為Action命名。
26
*/
27
public abstract void setActionId();
28
}
開發(fā)人員自己創(chuàng)建的Action都繼承自AbstractSecurityAction類,在此類的構(gòu)造方法中,調(diào)用SecurityManager.securityFiltration(this);方法,判斷當(dāng)前Action的ID是否與當(dāng)前用戶權(quán)限的中的ID相同,如果相同則調(diào)用action.setEnabled(true);,否則則是action.setEnabled(false);

/** *//**
* 通過傳入的IAction實例的id,判斷是否與用戶對應(yīng)的權(quán)限相同,如果存在與用戶權(quán)限中,則此IAction為可視,否則為不可視。
* @param action
*/

public static void securityFiltration(IAction action)
{

if(action!=null)
{
action.setEnabled(false);

if(action.getId()!=null && !action.getId().equals(""))
{
log.debug("當(dāng)前處理的Action為 : " + action.getId() + " 當(dāng)前用戶可用Action列表長度為 :" + UserInfo.getInstance().getActionList().size());

for(String str : UserInfo.getInstance().getActionList())
{

if(str.equals(action.getId()))
{
action.setEnabled(true);
log.debug("當(dāng)前Action : " + action.getId() + "與用戶可用Action列表中Action匹配,此Action可顯示");
return;
}
}
}
}
}
第二種情況的基類:
1
package com.glnpu.dmp.client.platform.core.internal.security.absaction;
2
3
import org.eclipse.jface.action.IAction;
4
import org.eclipse.jface.viewers.ISelection;
5
import org.eclipse.ui.IEditorActionDelegate;
6
import org.eclipse.ui.IEditorPart;
7
8
import com.glnpu.dmp.client.platform.core.internal.security.SecurityManager;
9
10
/** *//**
11
* 實現(xiàn)自<code>IEditorActionDelegate</code>重載selectionChanged方法,在其中加入用戶權(quán)限判斷。
12
* @author lign
13
*
14
*/
15
public abstract class AbstractSecurityEditorActionDelegate implements IEditorActionDelegate
{
16
17
/**//* (non-Javadoc)
18
* @see org.eclipse.ui.IEditorActionDelegate#setActiveEditor(org.eclipse.jface.action.IAction, org.eclipse.ui.IEditorPart)
19
*/
20
public abstract void setActiveEditor(IAction action, IEditorPart targetEditor);
21
22
/**//* (non-Javadoc)
23
* @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
24
*/
25
public abstract void run(IAction action);
26
27
/**//*
28
* 重載方法,加入權(quán)限判斷重置Action是否可用
29
*/
30
public void selectionChanged(IAction action, ISelection selection)
{
31
//判斷權(quán)限
32
SecurityManager.securityFiltration(action);
33
}
34
35
}
以上是擴(kuò)展editorAction擴(kuò)展點時要繼承的基類,其他兩種情況同它相同。
這種方法雖然笨拙,但是還是有用的。
Viewer的顯示總是依靠某一個透視圖的,因此就有了我們的透視圖基類:
1
package com.glnpu.dmp.client.platform.core.internal.security.absperspective;
2
3
import java.util.ArrayList;
4
import java.util.List;
5
6
import org.apache.log4j.Logger;
7
import org.eclipse.ui.IFolderLayout;
8
import org.eclipse.ui.IPageLayout;
9
import org.eclipse.ui.IPerspectiveFactory;
10
11
import com.glnpu.dmp.client.platform.core.internal.security.SecurityManager;
12
13
/** *//**
14
* 實現(xiàn)自<code>IPerspectiveFactory</code>,提供標(biāo)準(zhǔn)布局,及左側(cè)為Viewers,底部為Viewers
15
* @author lign
16
*
17
*/
18
public abstract class AbstractSecurityPerspective implements IPerspectiveFactory
{
19
20
private Logger log = Logger.getLogger(this.getClass());
21
22
protected String [] leftViewerIds = null;
23
protected String [] bottomViewerIds = null;
24
protected String perspectiveName = null;
25
26
/**//* (non-Javadoc)
27
* @see org.eclipse.ui.IPerspectiveFactory#createInitialLayout(org.eclipse.ui.IPageLayout)
28
*/
29
public void createInitialLayout(IPageLayout layout)
{
30
List<String> leftViewerList = getLeftViewers(leftViewerIds);
31
log.debug("當(dāng)前用戶可用的leftViewer列表長度為 : " + leftViewerList.size());
32
if(leftViewerList.size()>0)
{
33
IFolderLayout folderLayoutLeft = layout.createFolder(this.perspectiveName+"Left", IPageLayout.LEFT, 0.25f, layout.getEditorArea());
34
for(String str : leftViewerList)
{
35
folderLayoutLeft.addView(str);
36
layout.getViewLayout(str).setCloseable(false);
37
}
38
}
39
40
List<String> bottomViewerList = getBottomViewers(bottomViewerIds);
41
log.debug("當(dāng)前用戶可用的bottomViewer列表長度為 : " + bottomViewerList.size());
42
if(bottomViewerList.size()>0)
{
43
IFolderLayout folderLayoutBottom = layout.createFolder(this.perspectiveName+"Bottom", IPageLayout.BOTTOM, 0.80f, layout.getEditorArea());
44
for(String str : bottomViewerList)
{
45
folderLayoutBottom.addView(str);
46
layout.getViewLayout(str).setCloseable(false);
47
}
48
}
49
}
50
51
/** *//**
52
* 過濾不可顯示的左側(cè)Viewer
53
* @param leftViewerIds
54
* @return
55
*/
56
private List<String> getLeftViewers(String [] leftViewerIds)
{
57
List<String> _list = new ArrayList<String>();
58
for(String str : leftViewerIds)
{
59
if(SecurityManager.isSecurityViewer(str))
{
60
_list.add(str);
61
}
62
}
63
return _list;
64
}
65
66
/** *//**
67
* 過濾不可顯示的底部Viewer
68
* @param bottomViewerIds
69
* @return
70
*/
71
private List<String> getBottomViewers(String [] bottomViewerIds)
{
72
List<String> _list = new ArrayList<String>();
73
for(String str : bottomViewerIds)
{
74
if(SecurityManager.isSecurityViewer(str))
{
75
_list.add(str);
76
}
77
}
78
return _list;
79
}
80
}
其子類需要在構(gòu)造方法中給父類的leftViewerIds,bottomViewerIds和perspectiveName屬性付值,及傳給父類左側(cè)需要放置那些Viewer的ID,底部需要放置那些Viewer的ID,剩下的就是過濾了。
其實最麻煩的是過濾透視圖,透視圖是必須配置到plugin.xml中的,只要配置進(jìn)去,我們基本上就不能做什么操作了,在透視圖的菜單中有Other...項,會列出所有的透視圖。我們的解決辦法是:
1
/** *//**
2
* 在RCP程序的ApplicationWorkbenchWindowAdvisor類的postWindowCreate()方法中調(diào)用,用于刪除無權(quán)限操作的Perspective
3
* @param pr
4
*/
5
public static void perspectiveFiltration(PerspectiveRegistry pr)
{
6
IPerspectiveDescriptor[] _ipd = pr.getPerspectives();
7
Object [] _objs = new Object[_ipd.length];
8
for(int i=0; i<_ipd.length; i++)
{
9
if(!isSecurityPerspective(_ipd[i].getId()))
{
10
_objs[i] = _ipd[i];
11
}
12
}
13
log.debug("從已注冊的Perspective列表中刪除Perspectives為: " + _objs.toString());
14
pr.removeExtension(null, _objs);
15
log.debug("需要重新注冊新的默認(rèn)Perspective頁");
16
setDefautlPerspectice(pr);
17
}
取得透視圖注冊器,然后移除不可用的擴(kuò)展點。但是Eclipse在移除擴(kuò)展點時有一個BUG會彈出一個錯誤對話框,但是移除擴(kuò)展點的代碼照樣執(zhí)行,至少在3.2.2中這個BUG是存在的,而且在Eclipse的BUG管理器中,這個BUG已經(jīng)提出來了,聽說是正在修復(fù)。沒關(guān)系,還好它是開源的,到最后產(chǎn)品發(fā)布的時候它還沒有修改,我們就自己改了。
到此,安全文件就這樣解決了,希望大家說說自己的看法,肯定有比我更好的辦法解決,不妨貢獻(xiàn)出來,我們互相學(xué)習(xí)。
客戶虐我千百遍,我待客戶如初戀!