感谢大家最近对本系列的关注和评论,我会继续完善内容,并且总结教训写出更好的东东来。
今天谈谈最近在研究的RCP安全模型,其实RCP在诞生之初就是建立在一个非常鲁棒的框架之上的---OSGi,它不但有全新的概念,全新的思路,全新的热插拔技术,还有非常好的安全模型(
equinox security 项目好像还在孵化中) 。
解决RCP的安全问题,从最近的调研中看是有四种方式:
1.equinox security项目;
2.使用org.eclipse.ui.activities扩展点定义可访问权限和角色;
3.使用Eclipse-JAAS插件;
4.自己写代码在对功能点进行过滤;
第一种方案不可行,因为equinox security项目还在孵化中,而且没有充足的文档,因此放弃;
第二种方案可行,但是灵活性不够,我们很难在项目开始之初充分的定义出所有的角色以及角色对应的权限,而且还要引入大量的配置,所以也放弃;
第三种方案可能可行,因为从老外的文档中看出,这个插件是可用的,而且是强大的,它的原理就是定义了一堆自己的扩展点,然后通过分析扩展点,过滤掉不可用的功能点。但是这个插件的更新时间竟然是2005年,N久都没有人管过了~而且还是基于Eclipse3.0的实现,所以也放弃了;
我选择的是第四种方案,自己写代码对功能点进行过滤,其实思路很简单,我们定义基类,在基类中过滤掉有那些功能点可以被显示,或者不能被显示。
RCP应用中所需要关注安全点主要是:
1.Action
2.Viewer
3.编辑器
4.透视图
经过讨论,其实我们的关注点主要集中在Action和透视图,因为透视图总会包含一组Viewer出现所以我们只要在透视图布局类中将我们不需要关注的Viewer过滤掉,那么Viewer就可以被管理起来了,而编辑器的出现也主要是针对Viewer出现的,如果一个Viewer不能显示,那么和它对应的编辑器也永远无法显示,所以编辑器几乎不用考虑。
其实安全模型的核心就是Action,Viewer,透视图。
对于Action,存在两种,一种是开发人员自己在代码中创建出来的Action(new Action),另一种就是在plugin.xml中配置的。
第一种情况的基类:
1package com.glnpu.dmp.client.platform.core.internal.security.absaction;
2
3import org.apache.log4j.Logger;
4import org.eclipse.jface.action.Action;
5import org.eclipse.jface.resource.ImageDescriptor;
6
7import com.glnpu.dmp.client.platform.core.internal.security.SecurityManager;
8
9/** *//**
10 * 继承自<code>Action</code>,在构造方法中加入用户权限判断。
11 * @author lign
12 *
13 */
14public 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("当前处理的Action为: " + this.getId());
21 //权限判断
22 SecurityManager.securityFiltration(this);
23 }
24 /** *//**
25 * 实现者必须实现此方法,并在方法内调用setId方法,为Action命名。
26 */
27 public abstract void setActionId();
28}
开发人员自己创建的Action都继承自AbstractSecurityAction类,在此类的构造方法中,调用SecurityManager.securityFiltration(this);方法,判断当前Action的ID是否与当前用户权限的中的ID相同,如果相同则调用action.setEnabled(true);,否则则是action.setEnabled(false);
/** *//**
* 通过传入的IAction实例的id,判断是否与用户对应的权限相同,如果存在与用户权限中,则此IAction为可视,否则为不可视。
* @param action
*/
public static void securityFiltration(IAction action){
if(action!=null) {
action.setEnabled(false);
if(action.getId()!=null && !action.getId().equals("")) {
log.debug("当前处理的Action为 : " + action.getId() + " 当前用户可用Action列表长度为 :" + UserInfo.getInstance().getActionList().size());
for(String str : UserInfo.getInstance().getActionList()) {
if(str.equals(action.getId())) {
action.setEnabled(true);
log.debug("当前Action : " + action.getId() + "与用户可用Action列表中Action匹配,此Action可显示");
return;
}
}
}
}
}
第二种情况的基类:
1package com.glnpu.dmp.client.platform.core.internal.security.absaction;
2
3import org.eclipse.jface.action.IAction;
4import org.eclipse.jface.viewers.ISelection;
5import org.eclipse.ui.IEditorActionDelegate;
6import org.eclipse.ui.IEditorPart;
7
8import com.glnpu.dmp.client.platform.core.internal.security.SecurityManager;
9
10/** *//**
11 * 实现自<code>IEditorActionDelegate</code>重载selectionChanged方法,在其中加入用户权限判断。
12 * @author lign
13 *
14 */
15public 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 * 重载方法,加入权限判断重置Action是否可用
29 */
30 public void selectionChanged(IAction action, ISelection selection) {
31 //判断权限
32 SecurityManager.securityFiltration(action);
33 }
34
35}
以上是扩展editorAction扩展点时要继承的基类,其他两种情况同它相同。
这种方法虽然笨拙,但是还是有用的。
Viewer的显示总是依靠某一个透视图的,因此就有了我们的透视图基类:
1package com.glnpu.dmp.client.platform.core.internal.security.absperspective;
2
3import java.util.ArrayList;
4import java.util.List;
5
6import org.apache.log4j.Logger;
7import org.eclipse.ui.IFolderLayout;
8import org.eclipse.ui.IPageLayout;
9import org.eclipse.ui.IPerspectiveFactory;
10
11import com.glnpu.dmp.client.platform.core.internal.security.SecurityManager;
12
13/** *//**
14 * 实现自<code>IPerspectiveFactory</code>,提供标准布局,及左侧为Viewers,底部为Viewers
15 * @author lign
16 *
17 */
18public 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("当前用户可用的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("当前用户可用的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 * 过滤不可显示的左侧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}
其子类需要在构造方法中给父类的leftViewerIds,bottomViewerIds和perspectiveName属性付值,及传给父类左侧需要放置那些Viewer的ID,底部需要放置那些Viewer的ID,剩下的就是过滤了。
其实最麻烦的是过滤透视图,透视图是必须配置到plugin.xml中的,只要配置进去,我们基本上就不能做什么操作了,在透视图的菜单中有Other...项,会列出所有的透视图。我们的解决办法是:
1 /** *//**
2 * 在RCP程序的ApplicationWorkbenchWindowAdvisor类的postWindowCreate()方法中调用,用于删除无权限操作的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("需要重新注册新的默认Perspective页");
16 setDefautlPerspectice(pr);
17 }
取得透视图注册器,然后移除不可用的扩展点。但是Eclipse在移除扩展点时有一个BUG会弹出一个错误对话框,但是移除扩展点的代码照样执行,至少在3.2.2中这个BUG是存在的,而且在Eclipse的BUG管理器中,这个BUG已经提出来了,听说是正在修复。没关系,还好它是开源的,到最后产品发布的时候它还没有修改,我们就自己改了。
到此,安全文件就这样解决了,希望大家说说自己的看法,肯定有比我更好的办法解决,不妨贡献出来,我们互相学习。
客户虐我千百遍,我待客户如初恋!