【背景】
子系统和portal不在同一个域中且项目中要求不能使用nginx、apache等反向代理软件,故使用jsonp从代码角度解决ajax跨域问题
【实现思路】
通过jquery的jsonp来实现ajax的跨域访问,具体实现步骤如下:
1, 判断菜单url是否和portal同域。
2, 同域则通过原有方式获取菜单,不同域则通过jsonp方式获取菜单。
3, 修改不同域集成子应用菜单生成代码,满足jsonp格式要求。
【关键代码】
1、修改portal的代码(有一定代码量,不在页面上体现,请参考附件demo):
1)pui-all.js,新增命名空间Pui.datap,用于处理跨域ajax。其中转换json的调用方法JSON.stringify,该方法不支持低版本ie,需导入json2.js(见附件)。
2)修改menuTree.js的createStore方法,该方法用于菜单树的生成,如ajax地址跨域,则走else分支,调用Pui.datap命名空间(此命名空间在pui-all.js定义)。
3)修改menuTreeUIService.jsp,添加<script type="text/javascript" src="<%=contextPath%>/runtime/integration/pui/json2.js"></script>,支持低版本ie转化json。
上述三步完成后,portal端的代码改造就完成了,portal应用通过原方式进行ajax调用,非portal应用通过jsonp方式进行调用,因为jsonp方式需要对服务端返回值格式有特殊要求,故需修改集成子应用生成菜单的代码。
2、修改应用代码:
集成子应用代码以eos(default)应用为例,第三方应用方法相同。
增加jsonp_menu.jsp页面(portal里应用菜单路径为该页面地址),该页面实现了封装菜单变为jsonp格式代码。
<%@page import="javax.servlet.http.HttpServletResponse"%>
<%@page import="org.gocom.components.coframe.auth.login.LoginService"%>
<%@page import="java.util.List"%>
<%@page import="com.primeton.cap.auth.MenuTree.MenuTreeNode"%>
<%@page import="com.primeton.common.util.JsonTransUtil"%>
<%@page import="java.net.URLDecoder"%>
<%@page import="java.io.PrintWriter"%>
<%
String jsonp = request.getParameter("jsonpcallback");
String appCode = request.getParameter("appCode");
LoginService ls = new LoginService();
List<MenuTreeNode> mtn = ls.getUserMenuTreeByAppCode(appCode);
String temp = JsonTransUtil.List2Json(mtn,jsonp);
//System.out.println(temp);
response.setContentType("text/xml;charset=utf-8");
response.setHeader("Cache-Control", "no-cache");
PrintWriter po = response.getWriter();
po.write(temp);
po.flush();
po.close();
%>
其中用到了类JsonTransUtil.List2Json
该类实现代码如下,fastjson-1.2.2.jar见附件:
package com.primeton.common.util;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.eos.system.annotation.Bizlet;
@Bizlet("Json转换工具类")
public class JsonTransUtil{
@Bizlet("List转json")
public static String List2Json(List list,String jsonpcallback){
String json = JSON.toJSONString(list);
json = "{\"treeNodes\":"+json+"}";
json = json.replaceAll("\"","\'");
json = jsonpcallback + "("+json+")";
return json;
}
}
跨域解决方案.zip(代码供参考,因为还包括其他的修改,请不要直接覆盖到项目里)
备注:若portal主题使用多页签模式且没有使用反向代理,只是通过jsonp解决跨越问题,可能会出现session覆盖的问题;
问题场景:weblogic应用服务器,第一个页签中打开A应用a功能,在第二个页签中打开B应用b功能,再切换到第一个页签点击a功能,此时报错。
跟踪分析:通过监控发现,此时点击a功能系统会重新登陆,重新登陆请求是通过a功能发出的,因为a功能发送ajax请求跨域,导致重新登陆失败,功能报错。
查看响应文件发现,应用A和应用B的cookieName 都叫jsessionid,访问应用B的时候生成的jsessionid覆盖了A的jsessionid,再次访问A的时候A 应用的服务端不认识被覆盖后的jsessionid,故判断A应用未登陆,功能出现了问题。
解决办法: 修改weblogic的配置文件weblogic.xml中的cookiename,将每个应用的cookiename修改的不同,故jsessionid不会互相覆盖,不会出现上述问题详细场景: