利用WEBSERVICE实现J2EE分布式部署方案

问题概述 
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等代码太多,此处省略.

热门文章