问题概述 
1. 中国登记结算用的普元EOS平台作为开发工具[类似MyEclipse开发,经过多层封装,
 
      实现中国式流程图模型,开发过程通过拖拽流程,后台完善逻辑代码,如.net开发控件,jsf,struts等] 
2. 宇博[外包开发商]利用EOS开发GAS系统, 
3. 润乾报表要和GAS做集成,语言上都是JAVA,无缝集成应该没有太大问题,但是为了安全及稳定性,
 
      最终还是另外搭建润乾报表系统[决定权在客户],此部分由我负责 
4. 最终报表模板和润乾报表引擎分开, 发布或更变需要修改报表模板本身,
 
      但是不影响整个报表系统的发布,报表模板可以指定在绝对路径上,如:C:/reportFiles下,
 
      应用程序是放在WebSphere服务器下不去变动 
5. 两个平台为了实现统一的安全的权限管理,最终决定用Webservice服务实现,详见理论说明部分 
6. 结合GAS系统统一完成系统权限的统一控制,整合界面、报表格式、图形输入输出的要求,相关接口说明规范 
7. 权限细致到功能按钮级,即不同的人或部门具有不同的导出按钮,自定义按钮,按钮全部动态生成 
8. 此部分工作开发时间两周,更多内容详见:中国结算沪-JS-JSKF-JLBD-08-软件详细设计说明书.doc 
 
      案例
 
      
用此思想成功案例如下: 
上海铁路局[.net 对 java] 
上海交银施罗德基金公司[.net 对 java] 
中国登记结算上海分公司[java 对 java]
 
       
      报表系统自定义按钮所需数据字典
FunctionButton表(功能按钮存储表) 
字段名称 类型 是否为空 主外键 详细描述 
Btn_ID Int 否 PK 按钮ID号(自动增长) 
Btn_Title nvarchar(50) 否 此按钮的标题名称 
Btn_Image nvarchar(50) 否 此按钮图片 
Btn_Onclick nvarchar(120) 是 此按钮执行的JavaScript方法名 
Btn_Script nvarchar(5000) 是 此按钮执行的JavaScript方法体内容 
Btn_Name nvarchar(50) 否 根据Btn_Name查询需要功能按钮的列表,Btn_Name需要和GAS系统统一协定命名 
Bt_Type nvarchar(50) 否 根据Bt_Type判定此按钮的类型 
1为功能按钮,2为自定义按钮 
结合Btn_Name联合查询及Btn_ID排序
 
      表结构:
 
      
 
      表数据:
 
      
 
      FunctionRes表(报表与GAS系统定义资源编号的对应列表) 
字段名称 类型 是否为空 主外键 详细描述 
id Int 否 PK 资源ID(自动增长) 
resourceId nvarchar(50) 否 在GAS系统里定义的资源编号 
raqName nvarchar(16) 否 根据GAS传递的raqName查询出对应的resourceId作为访问GAS系统WebService的条件 
表结构:
 
      
 
 
      表数据:
 
      
 
      程序设计部分
 
       
      架构图解:
 
       
      访问报表资源规范: 
1.访问报表资源格式http://服务器ip地址/runqianReport4/indexSSO.jsp?fwcs=3.3.raq&sessionId=b96335b0db0347&userName=zhansan
 
      再访问系统其它页面。 
2.通过WEBSERVICES跨域实现资源权限控制,GAS系统作为WEBSERVICES服务端,掌管各种权限, 
3.通过调用GAS系统给的资源指令来决定报表系统要返回的资源结果,报表不做权限控制,完全有GAS指令决定,
 
      主要事针对功能按钮的控制,使其权限达到统一 
4.输入参数说明: 
sessionId当前GAS系统的session值,确定唯一性 
userName当前登录GAS系统的用户信息 
resourceId报表资源在GAS系统菜单的资源编号信息 
4.输出参数: 
Message报表系统请求GAS之后返回的结果信息 
Output报表系统请求GAS之后返回的功能按钮列表 返回值为Excel|Print|Word|Pdf等 
Style 报表系统换肤要用到的Css 样式表, 返回值为s1,s2等 
Haspermission报表系统请求GAS之后返回是否有权限访问资源,返回值为true or false 
5.输出相关参数都是通过统一加密,解密处理,如: 
按钮列表,用户名等信息 
实现方式原理步骤描述: 
1. 公司员工访问GAS管理平台 
2. GAS管理平台生成一个唯一值sessionId标记,并将sessionId标记与当前Windows登录的员工帐号进行对应,
 
      并记入数据库表中. 
3. GAS管理平台通过indexSSO.jsp?fwcs=3.3.raq&sessionId= b96335b0db0347bf&username= zhangsan
 
      方式访问报表系统单点登录接口页面。 
4. GAS管理平台和报表系统双方需要约定资源resourceId,报表名称raq 
5. 报表系统在indexSSO.jsp页面代码中获取页面参数传递过来的sessionId等相关信息值。 
6. 报表系统调用GAS管理平台WebServices的SSO.ASMX?WSDL调用, sessionId, username, 
 
      resourceId值作为参数. 
7. GAS管理平台WebService通过上一步传来的sessionId, username, resourceId参数,
 
      在数据库表中查询步骤2记录的对应的员工帐号, 功能权限按钮列表,是否具有权限等信息,作为返回值. 
8. 报表系统获取上一步返回的员工帐号,权限等信息在报表系统建立此操作的Session环境,
 
      还回请求的报表,即完成单点登录的权限控制 
9. GAS管理平台随时可调用报表系统此Session环境内权限功能。 
10. 报表系统上的自定义功能按钮也随时可调用GAS管理平台的WebService获取权限 
此方案主要是针对某些对安全性要求较高的控制: 
1整体布局图:
 
      
 
      返回详细参数的功能按钮结合图例说明: 
按钮整体排版规则先后:自定义按钮--润乾导出功能按钮--缩放比例--分页 
Style=s2换肤按钮功能样式:
 
      
 
      Style=s1换肤按钮功能样式:
 
      
 
      功能按钮: 
Output=Excel|Print|Word|Pdf|Text|Scale|Page|custom1| custom2 
Excel:是否具有导出Excel功能 
Print:是否具有打印功能 
Word:是否具有导出Word功能 
Pdf:是否具有导出Pdf功能 
Text:是否具有导出Text功能 
Scale:是否具有缩放比例功能 
Page:是否具有分页功能 
custom1| custom2:自定义功能按钮,可任意书写JavaScript或超链接等信息实现,内容通过后台数据库表进行维护 
2.WebService相关包: 
通过引用XFire能够实现真正意义上的WebService功能,需要加入相关XFire包,经过筛选jar包如下:
 
      
 
       
      流程演示
1,保证当前的WebService是可用的:
 
      
 
       
      2,统一入口为:
http://localhost:9090/runqianReport4/index.jsp?fwcs=3.3.raq&sessionId=2uei1e3&userName=Admin 
经过处理查询调用WebService之后跳转到报表展现页面:
 
      此过程中的一些参数传递都是经过加密处理:fwcs,button,style 

 
      3.根据权限的不同分配:每张报表所具有的功能按钮不一样: 
点击查看ABC报表:
 
      
 
      ABC报表没有自定义按钮权限:
 
      
 
      点击查看23报表:

 
      23报表只有分页和缩放权限:
 
      
 
       
      程序代码段
 
      1.访问报表资源格式http://服务器ip地址 
/runqianReport4/indexSSO.jsp?fwcs=3.3.raq&sessionId=b96335b0db0347&userName=zhangsan为入口,
 
      由index.Jsp接受相关信息,代码代码段:
 
      
<% 
//查看相关信息是否在session中,如果有直接取,没有就请求拿到 
String sessionId=request.getParameter("sessionId"); 
if (sessionId == null
sessionId=session.getAttribute("sessionId").toString(); 
String userName=request.getParameter("userName"); 
if (userName == null
userName=session.getAttribute("userName").toString(); 
String fwcs=request.getParameter("fwcs"); 
fwcs = fwcs == null "" : fwcs; 
//定义临时参数变量,主要是为了以后的扩张功能,如报表需要钻取数据要传递的参数信息 
String reportClient1=request.getParameter("reportClient1"); 
reportClient1 = reportClient1 == null "" : reportClient1; 
String reportClient2=request.getParameter("reportClient2"); 
reportClient2 = reportClient2 == null "" : reportClient2; 
String reportClient3=request.getParameter("reportClient3"); 
reportClient3 = reportClient3 == null "" : reportClient3; 
String reportClient4=request.getParameter("reportClient4"); 
reportClient4 = reportClient4 == null "" : reportClient4; 
String reportClient5=request.getParameter("reportClient5"); 
reportClient5 = reportClient5 == null "" : reportClient5; 
//报表名称加密 
String Base64fwcs = Base64Util.Base64Encode(fwcs); 
String resourceId=ReportFunDAO.resIdResult(fwcs);//根据fwcs到数据库查询出来 
System.out.println("传resourceId:" + resourceId+"**sessionId:"+sessionId+"**userName:"+userName); 
//调用WebServices 
CheckPermissionClient client = new CheckPermissionClient(); 
CheckPermissionPortType service = client.getCheckPermissionHttpPort(); 
PermissionOutMsg pMsg=service.reportResults(resourceId, sessionId, userName); 
//调用成功返回相关结果集 
String message=pMsg.getMessage().getValue(); 
String output=pMsg.getOutput().getValue(); 
//功能按钮加密,为了保证同一个用户同一个session环境中进行的相关操作, 
// 有用信息直接放到session中, 
String Base64Output = Base64Util.Base64Encode(output); 
//Css样式表 
String cssCss=pMsg.getStyle().getValue(); 
String Base64cssCss=Base64Util.Base64Encode(cssCss); 
boolean flag=pMsg.isHasPermission(); 
session.setAttribute("flag", flag); 
session.setAttribute("sessionId", sessionId); 
session.setAttribute("userName", userName); 
session.setAttribute("exception", message); 
System.out.println("Msg:"+message); 
System.out.println("功能列表:"+output); 
System.out.println("Css:"+cssCss); 
System.out.println("是否具有权限:"+flag); 
System.out.println("当前用户名:"+userName); 
System.out.println("sessionId:"+sessionId); 
System.out.println("Base64fwcs:"+Base64fwcs); 
%> 
2.相关GAS服务器的代码和报表客户端的代码,此处省略,可参见源码 
3.根据调用Webservice返回的boolean flag=pMsg.isHasPermission(); 
通过过滤器OnlineFilter决定是否给报表计算的结果,相关代码段: 
public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException, ServletException, 
NullPointerException { 
RequestDispatcher dispatcher = request 
.getRequestDispatcher("myError2.jsp"); 
HttpServletRequest req = (HttpServletRequest) request; 
HttpServletResponse res = (HttpServletResponse) response; 
HttpSession session = req.getSession(true); 
// 从session里取的权限判断信息 
boolean flag = (Boolean) session.getAttribute("flag"); 
System.out.println("flag:" + flag); 
if (!flag) { 
// 跳转到登陆页面 
dispatcher.forward(request, response); 
res.setHeader("Cache-Control", "no-store"); 
res.setDateHeader("Expires", 0); 
res.setHeader("Pragma", "no-cache"); 
System.out.println("用户没有登陆,不允许操作"); 
return
} else { 
chain.doFilter(request, response); 
System.out.println("用户已经登陆,允许操作"); 
} 
} 
4.如果过滤器验证请求失败,则跳转错误页面myError2.jsp,代码如下: 
<div class="class1"> 
<% 
String e = session.getAttribute( "exception" ).toString(); 
out.println( "<h1>信息:</h1><div style='color:red'>" + e + "</div>" ); 
%> 
<span id="stateBut" onclick="$use()">查看详细信息</span> 
<p id="class1content" style="display:none"></p> 
</div> 
如果过滤器验证请求成功,则通过index.jsp分发统一跳转,相关信息都是加密过的,代码段: 
<%
String fullfilePath =request.getContextPath()+"/reportJsp/showReport.jsp?fwcs="+Base64fwcs+"&button="+Base64Output+"&cssCss="+Base64cssCss
 
      +"&reportClient1="+reportClient1+"&reportClient2="+reportClient2+"&reportClient3="+reportClient3+"&reportClient4="+reportClient4+"&reportClient5="+reportClient5; 
System.out.println("fullfilePath:" + fullfilePath); 
response.sendRedirect(fullfilePath); 
%> 
5. showReport.jsp是真正发布报表模板的,通过接收到的参数信息,解密,到数据库查询出当前用户当前报表所拥有的按钮信息排列出来,关于自定义按钮的,是通过定义JavaScript的方式,以供能灵活操作,showReport.jsp代码段: 
<% 
request.setCharacterEncoding("GBK"); 
//报表名称 
String Base64fwcs = request.getParameter("fwcs"); 
String report = Base64Util.Base64Decode(Base64fwcs); 
//导出功能按钮 
String Base64Output = request.getParameter("button"); 
String roleNames = Base64Util.Base64Decode(Base64Output); 
//Css样式表cssCss 
String Base64cssCss=request.getParameter("cssCss"); 
String cssCss=Base64Util.Base64Decode(Base64cssCss); 
//报表模板目录 
String reportFileHome = Context.getInitCtx().getMainDir(); 
StringBuffer param = new StringBuffer(); 
//保证报表名称的完整性 
int iTmp = 0; 
if ((iTmp = report.lastIndexOf(".raq")) <= 0) { 
report = report + ".raq"
iTmp = 0; 
} 
//为了报表模板或者参数模板中要用到外部传入的一些参数信息, 
//如当前的用户名信息来查询相关的数据等,直接可以将userName和sessionId拼在参数串上, 
//报表在引用这些参数值时,直接在定义全局参数,把名字定义一样即可,如:userName 
Enumeration paramNames = request.getParameterNames(); 
if (paramNames != null) { 
while (paramNames.hasMoreElements()) { 
String paramName = (String) paramNames.nextElement(); 
String paramValue = request.getParameter(paramName); 
if (paramValue != null) { 
//把参数拼成name=value;name2=value2;.....的形式 
param.append(paramName).append("=").append(paramValue) 
.append(";"); 
} 
} 
param.append("userName=").append(session.getAttribute("userName=").toString()).
 
      append(";sessionId=").append(session.getAttribute("sessionId").toString()).append(";"); 
} 
System.out.println("Param:"+param.toString()); 
//以下代码是检测这个报表是否有相应的参数模板,参数模板的规定是以当前报表模板_arg的 
//模板,即为参数模板,这样当前的参数模板和报表模板能够进行自动匹配, 
//如:报表模板:123.raq,参数模板:123_arg.raq 
String paramFile = report.substring(0, iTmp) + "_arg.raq"
File f = new File(reportFileHome 
+ File.separator + paramFile); 
%> 
<% 
// 报表缩放控制 
String scaleMode = request.getParameter("rq_scale_mode"); 
scaleMode = scaleMode == null ? "4" : scaleMode; 
String scale = request.getParameter("rq_scale"); 
scale = scale == null ? "1.0" : scale; 
%> 
<div class="report-tools" id=titleTable> 
<div class="report-tools-button"> 
<% 
// 功能菜单样式的控制 
ReportFunctionBar funcBar = new ReportFunctionBar(request, cssCss); 
out.print(funcBar.build(request,roleNames)); 
%> 
</div> 
<div class="clearboth"></div> 
</div> 
按钮通过ReportFunctionBar类来执行,打印结果集,代码段: 
/** 
* 生成报表工具条 
* @param roleNames - 资源类型 
* @return 
*/ 
public String build(HttpServletRequest request,String roleNames) { 
StringBuffer html = new StringBuffer(); 
try { 
//传递条件 
roleNames = roleNames == null ? "" : roleNames; 
System.out.println("roleNames:"+roleNames); 
//自定义按钮 
List fBtnLst2=reportfO.areaReportResults2(roleNames); 
request.getSession().setAttribute("newsList", fBtnLst2); 
for(int i=0;i<fBtnLst2.size();i++){ 
ReportFuncIcon rfi = (ReportFuncIcon)fBtnLst2.get(i); 
html.append(" <a href=\"#\" onclick=\""+rfi.getBtOnClick()+"\">"+ 
      "<img src=\""+stylePath+"images/"+rfi.getBtImage()+"\" border=\"no\" alt=\"" 
+rfi.getBtTitle()+"\">"+"</a>\n"); 
} 
//导出功能按钮 
List fBtnLst1=reportfO.areaReportResults1(roleNames); 
for(int i=0;i<fBtnLst1.size();i++){ 
ReportFuncIcon rfi = (ReportFuncIcon)fBtnLst1.get(i); 
html.append(" <a href=\"#\" onclick=\""+rfi.getBtOnClick()+"\">"+
 
      "<img src=\""+stylePath+"images/"+rfi.getBtImage()+"\" border=\"no\" alt=\"" 
+rfi.getBtTitle()+"\">"+"</a>\n"); 
}} 
String[] roleNamesTemp = roleNames.split("\\|"); 
request.getSession().setAttribute("roleNamesList", roleNamesTemp); 
for(int i = 0; i < roleNamesTemp.length; i++) { 
String roleName = roleNamesTemp[i]; 
//缩放比例 
if (TYPE_SCALE_REPORT.equals(roleName)) 
html.append(" <span class=\"report-tools-span\">缩放比例:</span><select name=\"rq_scale_mode\" onchange=\"zoom(this.value);\"></select>\n"); 
// 翻页功能,// 显示页数 
if (TYPE_PAGE_REPORT.equals(roleName)){ 
html.append(" <a href=\"#\" onclick=\"try{report1_toPage(1);}catch(e){} return false;\">"+firstPageImage+"</a>"); 
html.append(" <a href=\"#\" onclick=\"try{report1_toPage(report1_getCurrPage()-1);}catch(e){} return false;\">"+prevPageImage+"</a>"); 
html.append(" <a href=\"#\" onclick=\"try{report1_toPage(report1_getCurrPage()+1);}catch(e){} return false;\">"+nextPageImage+"</a>"); 
html.append(" <a href=\"#\" onclick=\"try{report1_toPage(report1_getTotalPage());}catch(e){} return false;\">"+lastPageImage+"</a>"); 
html.append(" <span class=\"report-tools-span\">共<span id=\"t_page_span\"></span>页/第<span id=\"c_page_span\"></span>页</span>"); 
} 
} 
自定义按钮所用到的JavaScript也是同时生成到jsp里,代码段: 
<script language="javascript"> 
<% 
List fBtnLst2 = (List)session.getAttribute("newsList"); 
for (int i=0;i<fBtnLst2.size();i++){ 
ReportFuncIcon rfi = (ReportFuncIcon)fBtnLst2.get(i); 
out.println(rfi.getBtScript()); 
} 
%> 
</script> 
发布报表通过jsp里的标签实现,代码段: 
<% 
//如果参数模板存在,则显示参数模板 
if (f.exists()) { 
%> 
<table id="param_tbl" width="100%" height="100%"> 
<tr> 
<td> 
<report:param name="form1" paramFileName="<%=paramFile%>" 
needSubmit="no" params="<%=param.toString()%>
hiddenParams="<%=param.toString()%>" /> 
</td> 
<td> 
<a href="javascript:_submit( form1 )"><img 
src="<%=request.getContextPath()%>/style/<%=cssCss%>/images/query.jpg" border=no 
style="vertical-align: middle"> </a> 
</td> 
</tr> 
</table> 
<% 
} 
%> 
<report:html name="report1" reportFileName="<%=report%>
funcBarLocation="" needPageMark="yes" generateParamForm="no" 
scale="<%=scale%>" params="<%=param.toString()%>" savePrintSetup="yes
 
      width="-1"textDataLineBreak="\r\n" 
exceptionPage="/reportJsp/myError2.jsp" />
 
       
      
6.相关查询DAO、加密解密Util、写webservice等代码太多,此处省略.