首页 » 技术分享 » 对天乙社区bbscs8实现的详细分析十五

对天乙社区bbscs8实现的详细分析十五

 

我们这里有必要讲一下IPSeeker这个工具类,它在com.laoer.bbscs.comm包中。它在程序中通过了
ui.setUserFrom(this.getIpSeeker().getCountry(this.getUserRemoteAddr()));
由IP得到用户来自哪里的解决方案.(用来封装ip相关信息,目前只有两个字段,ip所在的国家country和

地区area),注意它读取的文件是WEB-INF/IPDate/QQwry.Dat,下面是其属性的定义:
// 一些固定常量,比如记录长度等等
private static final int IP_RECORD_LENGTH = 7;
private static final byte REDIRECT_MODE_1 = 0x01;
private static final byte REDIRECT_MODE_2 = 0x02;
// Log对象
private static Log log = LogFactory.getLog(IPSeeker.class);
// 用来做为cache,查询一个ip时首先查看cache,以减少不必要的重复查找
private Hashtable<String, IPLocation> ipCache;
// 随机文件访问类
private RandomAccessFile ipFile;
// 内存映射文件
private MappedByteBuffer mbb;
// 起始地区的开始和结束的绝对偏移
private long ipBegin, ipEnd;
// 为提高效率而采用的临时变量
private IPLocation loc;
private byte[] buf;
private byte[] b4;
private byte[] b3;
-->
public IPSeeker() {
ipCache = new Hashtable<String, IPLocation>();
loc = new IPLocation();
buf = new byte[100];
b4 = new byte[4];
b3 = new byte[3];
try {
   // ClassPathResource cpr = new ClassPathResource("/" + IPDATE_FILE);
   // System.out.println(cpr.getFile());
   ipFile = new RandomAccessFile(IPDATE_FILE_PATH + IPDATE_FILE, "r");
   // ipFile = new RandomAccessFile(cpr.getFile(), "r");//随机读!
} catch (FileNotFoundException e) {
   // 如果找不到这个文件,再尝试再当前目录下搜索,这次全部改用小写文件


   // 因为有些系统可能区分大小写导致找不到ip地址信息文件
   String filename = new File(IPDATE_FILE_PATH + IPDATE_FILE).getName

().toLowerCase();
   File[] files = new File(IPDATE_FILE_PATH).listFiles();
   for (int i = 0; i < files.length; i++) {
    if (files[i].isFile()) {
     if (files[i].getName().toLowerCase().equals

(filename)) {
      try {
       ipFile = new RandomAccessFile(files

[i], "r");
      } catch (FileNotFoundException e1) {
       log.error("IP地址信息文件没有找到,

IP显示功能将无法使用");
       ipFile = null;
      }
      break;
     }
    }
   }
}
// 如果打开文件成功,读取文件头信息
if (ipFile != null) {
   try {
    ipBegin = readLong4(0);
    ipEnd = readLong4(4);
    if (ipBegin == -1 || ipEnd == -1) {
     ipFile.close();
     ipFile = null;
    }
   } catch (IOException e) {
    log.error("IP地址信息文件格式有错误,IP显示功能将无法使用");
    ipFile = null;
   }
}
}
好的,我们转到要用的getCountry()方法上:
public String getCountry(String ip) {
return getCountry(getIpByteArrayFromString(ip));//String->Byte[]
}
-->
public static byte[] getIpByteArrayFromString(String ip) {
byte[] ret = new byte[4];
StringTokenizer st = new StringTokenizer(ip, ".");
try {
   ret[0] = (byte) (Integer.parseInt(st.nextToken()) & 0xFF);
   ret[1] = (byte) (Integer.parseInt(st.nextToken()) & 0xFF);
   ret[2] = (byte) (Integer.parseInt(st.nextToken()) & 0xFF);
   ret[3] = (byte) (Integer.parseInt(st.nextToken()) & 0xFF);
} catch (Exception e) {
   log.error(e.getMessage());
}
return ret;
}
进而到了public String getCountry(byte[] ip) {
// 检查ip地址文件是否正常
if (ipFile == null) {
   return "bad.ip.file";
}
// 保存ip,转换ip字节数组为字符串形式,注意现在是字节!!!而非10进制数了哦~
String ipStr = getIpStringFromBytes(ip);
// 先检查cache中是否已经包含有这个ip的结果,没有再搜索文件
if (ipCache.containsKey(ipStr)) {
   IPLocation loc = (IPLocation) ipCache.get(ipStr);
   return loc.country;
} else {
   IPLocation loc = getIPLocation(ip);
   ipCache.put(ipStr, loc.getCopy());
   return loc.country;
}
}
而IPLocation是个内部类!
private class IPLocation {
public String country;

public String area;

public IPLocation() {
   country = area = "";
}

public IPLocation getCopy() {
   IPLocation ret = new IPLocation();
   ret.country = country;
   ret.area = area;
   return ret;
}
}
若缓存中没有则-->
private IPLocation getIPLocation(byte[] ip) {
IPLocation info = null;
long offset = locateIP(ip);
if (offset != -1) {
   info = getIPLocation(offset);
}
if (info == null) {
   info = new IPLocation();
   info.country = "";
   info.area = "";
}
return info;
}
好,关键的是一个private long locateIP(byte[] ip)这个方法将根据ip的内容,定位到包含这个ip国家

地区的记录处,返回一个绝对偏移 方法使用二分法查找。而private IPLocation getIPLocation(long

offset)返回IPLocation!OK!对于具体地代码深入分析有待研究!
OK!我们完成了用户的注册了。
好的,我们 <result name="success" type="redirect">
    /regSucceed.jsp //根目录下!
   </result>
而regSucceed.jsp向我们输出了提示成功字样。其中的路径问题有待小心哦~我们login进入主页面,继续

上次的main.bbscs分析之,我们先看struts.xml:
<package name="main" extends="bbscs-default" namespace="/">
<default-interceptor-ref name="userAuthInterceptorStack"></default-

interceptor-ref>
这里表明整个包都需要userAuthInterceptorStack:
   <interceptor-stack name="userAuthInterceptorStack">
    <interceptor-ref name="defaultStack"></interceptor-ref>
    <interceptor-ref name="userLoginInterceptor"></interceptor-

ref>
    <interceptor-ref

name="userPermissionInterceptor"></interceptor-ref>
   </interceptor-stack>
我们先看main:
<action name="main" class="com.laoer.bbscs.web.action.Main">//不需要spring的

代理!
   <result name="success">/WEB-INF/jsp/main.jsp</result>
</action>
它有四个属性:inUrl,bid(long),postID,nagUrl,应该也是与JSP界面交互的吧!我们看execute:
public String execute() {
if (this.getAction().equalsIgnoreCase("read")) {
   if (this.bid == 0 && StringUtils.isBlank(this.postID)) {
    this.setInUrl(BBSCSUtil.getActionMappingURLWithoutPrefix

("in"));
   } else {
    this.setInUrl(BBSCSUtil.getActionMappingURLWithoutPrefix

("read?action=topic&bid=" + this.bid + "&id="
      + this.postID));
   }
} else if (this.getAction().equalsIgnoreCase("forum")) {
   if (this.bid == 0) {
    this.setInUrl(BBSCSUtil.getActionMappingURLWithoutPrefix

("in"));
   } else {
    this.setInUrl(BBSCSUtil.getActionMappingURLWithoutPrefix

("forum?action=index&bid=" + this.bid));
   }
} else {
   if (StringUtils.isBlank(this.getInUrl())) {
    this.setInUrl(BBSCSUtil.getActionMappingURLWithoutPrefix

("in"));
   }
}
if (Constant.USE_URL_REWRITE) {
   this.setNagUrl("nag.html");
}
else {
   this.setNagUrl(BBSCSUtil.getActionMappingURLWithoutPrefix("nag"));
}
return SUCCESS;
}
这里主要根据bid和action(还有postID)决定inUrl和nagUrl,当然一开始是
if (StringUtils.isBlank(this.getInUrl())) {
    this.setInUrl(BBSCSUtil.getActionMappingURLWithoutPrefix

("in"));
   }
this.setNagUrl(BBSCSUtil.getActionMappingURLWithoutPrefix("nag"));//系统默认设置
进入main.jsp我们发现,它其实是由一个大table中的一个tr有三个td:第一个是

frmTitle,iframe=nagFrame而它的src是一个action:
<td align="middle" noWrap vAlign="center" id="frmTitle" height="100%">
      <iframe id="nagFrame" name="nagFrame" frameBorder="0" scrolling="auto"

src="<s:property value="%{nagUrl}"/>" class="iframe1"></iframe>
    </td>
而第二个td里面加了个table,里面的tr->td <td οnclick="switchSysBar()">
用于屏幕切换,第三个td为mainFrame:
<td style="width: 100%">
      <iframe frameBorder="0" id="mainFrame" name="mainFrame" scrolling="yes"

src="<s:property value="%{inUrl}"/>" class="iframe2"></iframe>
    </td>
我们再看看JavaScript:
<script language="JavaScript" type="text/javascript">
if(self!=top){
top.location=self.location;
}
function switchSysBar(){
if (switchPoint.innerHTML=='&lt;'){ //注意&lt;->;&gt;-<
    switchPoint.innerHTML='&gt;'
    document.getElementById("frmTitle").style.display="none";
}
else{
    switchPoint.innerHTML='&lt;'
    document.getElementById("frmTitle").style.display="block";
}
}

function changeMainFrameSrc(url) {
//alert(url);
document.getElementById("mainFrame").src = url;
}
接下来,我们分别分析/nag和/in。先从struts.xml看起:
<action name="nag" class="nagAction">
      <interceptor-ref name="userAuthInterceptorStack"></interceptor-ref>
   <interceptor-ref name="userSessionInterceptor"></interceptor-ref>
   <result name="success">/WEB-INF/jsp/nag.jsp</result>
另外一个:
<action name="in" class="inAction">
   <interceptor-ref name="mainUserAuthInterceptorStack"></interceptor-

ref>
   <interceptor-ref name="requestBasePathInterceptor"></interceptor-

ref>
   <result name="success">/WEB-INF/jsp/in.jsp</result>
</action>
在看action-servlet.xml,这里我们应该发现所有被spring代理的action的格式基本上都是:
<bean id="nagAction"
class="com.laoer.bbscs.web.action.Nag" scope="prototype"
autowire="byName">
</bean>
<bean id="inAction"
class="com.laoer.bbscs.web.action.In" scope="prototype"
autowire="byName">
</bean>
我们先看userAuthInterceptorStack:
<interceptor-stack name="userAuthInterceptorStack">
    <interceptor-ref name="defaultStack"></interceptor-ref>
    <interceptor-ref name="userLoginInterceptor"></interceptor-

ref>
    <interceptor-ref

name="userPermissionInterceptor"></interceptor-ref>
   </interceptor-stack>
它们的scope="prototype" autowire="byName"一个请求一个对象,且自动注入类型:byName,进入

userLoginInterceptor, 这里它完全用了一个通行证的做法,十分强大!我们看laoer的精彩说法:
天乙通行证是一个单点登录系统(Single sign on),用户登录一次通行证,即可以直接使用社区、Blog

等产品,无需再次注册或登录,保证几个产品用户的统一,但天乙通行证由于本身没有产品化,所以现在

没有开源,如果使用天乙社区需要做用户整合,只要实现通行证的类似系统就可以。 SSO的实现技术有多

种,也有一些开源产品,而天乙通行证使用的是比较简单,但非常有效的方案,即同域名下的Cookie方式

,实现用户的单点登录。 以天乙通行证为例,天乙社区(bbs.laoer.com)、天乙Blog(blog.laoer.com

)都运行在laoer.com域名下,用户在www.laoer.com上注册后登录,通行证就会在用户的客户端写入两个

Cookie,一个是用户名(Cookie名:PASS_USERNAME),一个是加密的用户名(Cookie名:

PASS_USERNAME_DES),加密的方式采用3DES,采用3DES加密是因为3DES可逆的加密算法,即对密文可以

还原,Cookie的Domain为.laoer.com,这样在laoer.com下的所有域名都可以访问到这个Cookie。
接下来我们说一下社区的认证过程,社区采用Struts2的MVC结构,在Action之前,都会执行一个拦截器(

UserLoginInterceptor),这个拦截器首先会读取通行证的两个Cookie,如果两个Cookie都存在,则对加

密的用户名进行解密,采用的密钥与通行证加密的密钥一致,然后解密后的用户名与另一个明文的用户名

比对,如果两个值一致,说明用户已经在通行证登录,则建立在社区的Session(如果Session已经存在,

则认为用户在社区也已经登录),如果通行证的Cookie不存在或是用户比对错误,则认为用户没有登录或

已经签退,社区会做游客登录,详细处理流程请阅读UserLoginInterceptor.java源码。
如果用户在现有系统上整合社区,只要顶层域写入两个Cookie,在社区后台中有设置通行证的项目,两个

系统采用的密钥一致就行了,同时3DES算法可以用多种语言实现,比如.NET、Ruby等等,所以可以在编程

语言异构的网络系统中无缝使用,当然用户也可以采用自己的加密解密算法。
这个方案要注意的问题:
1、只能使用在一个主域名下,如果要跨域名则不能实现。
2、需要用户支持Cookie。
3、密钥安全性,由于所有应用都使用统一的密钥,需要在加强管理,定期更换,或是采用RSA方法对密钥

加密,减少密钥泄漏的可能性。
4、对于非WEB系统,比如客户端程序,需要扩展其他方法实现。
原文见:http://bbs.laoer.com/main-read-15-ff80808113baa8140114123973985584.html
下面是我的理解:userLoginInterceptor首先引入request,response,servletContext,wc--

>sysConfig,us,uc-->一些服务!注意一点,通行证信息放在Cookie中....其判断流程是这样的:分两类,

由sysConfig.isUsePass()决定!
(1)若使用通行证,根据uc.isLoginPass再分为通行证是否登录,若登录即uc.isLoginPass为true,按

us=null分两类,有session和没session,当us=null时,根据uc获得用户信息:
if (us == null) {// 用户session没有在社区存在
      UserInfo ui =

userService.findUserInfoByUserName(uc.getPusername());
      if (ui != null) {//进而分为用户是否存在,存在

的话,做登录,且更新session和cookie

ui = userService.saveAtLogin(ui); // 用户登录处理
        uo =

userOnlineService.createUserOnline(uo); // 加入在线用户表

        us =

userService.getUserSession(ui);
        us.setLastActiveTime

(nowTime);
        us.setValidateCode

(uo.getValidateCode());

        ac.getSession().put

(Constant.USER_SESSION_KEY, us);

       
        uc.addCookies(ui);
} else { // 用户不存在,是新用户
       isNewUser = true;//等下一起创建之!
      }
这是没有session的情况,如果有session呢,也就是说用户已经在社区登录之中!
if (!us.getUserName().equals(uc.getPusername())) {// 用户在社区中的登录名和通行证中的用户名

不一致.这时仍然由us.getusername得到用户信息
UserInfo ui = userService.findUserInfoByUserName(uc.getPusername());
   if (ui != null) { // 用户存在,重新登录,原来的session删除之,以

cookie为准
uc.removeAllCookies();//去掉原来BBSokie信息
/**
public void removeAllCookies() {
addC(BBSCS_FORUMPERNUM_KEY, "", 0);
addC(BBSCS_POSTPERNUM_KEY, "", 0);
addC(BBSCS_TIMEZONE_KEY, "", 0);
addC(BBSCS_LASTSENDNOTETIME_KEY, "", 0);
addC(BBSCS_LASTPOSTTIME_KEY, "", 0);
addC(BBSCS_FORUMVIEWMODE_KEY, "", 0);
addC(BBSCS_EDITTYPE, "-1", 0);
addC(BBSCS_AUTHCODE, "", 0);
addC(BBSCS_USERNAME, "", 0);
addC(BBSCS_PASSWD, "", 0);
}
*/
}else{isNewUser=true}
if(isNewUser){// 创建社区用户,uc.getPusername()
}
这样并没有us.getUserNmae().equals(us.getPusername())的判断,说明us以及uc不需要改变!
UserSession us = (UserSession) ac.getSession().get(Constant.USER_SESSION_KEY);
UserCookie uc = new UserCookie(request, response, sysConfig);
若uc.isLoginPass()为假的话,通行证未登录,做游客登录
if (us == null) {// 用户没有登录过,直接做游客登录,有uo,us,uc.addGuestCookies();}
而用户在社区是登录状态us(存在的话),需要强制做游客登录
if (us.getGroupID() != Constant.USER_GROUP_GUEST) {// //如果原来用户不是游客,先清除原

Session,做游客登录
       // userSessionCache.remove

(uc.getUserName());
       ac.getSession().remove

(Constant.USER_SESSION_KEY);
       uc.removeAllCookies();
对于不使用通行证的话,根据us=null来决定,其子判断uc.isSaveLoginCookie()
: public boolean isSaveLoginCookie() {
if (StringUtils.isNotBlank(this.userName) && StringUtils.isNotBlank

(this.passwd)) {
   return true;
} else {
   return false;
}
有的话,从cookie中取,没有的话,Guest!我们看其中的一段代码:
uo = userOnlineService.createUserOnline(uo);
       us = this.createGuestUserSession

(uo.getUserID(), uo.getUserName(), userService);
/**
private UserSession createGuestUserSession(String gUestID, String gUesrName, UserService

userService) {
UserSession us = new UserSession();

us.setGroupID(Constant.USER_GROUP_GUEST);
us.setId(gUestID);
us.setNickName("Guest");
us.setUserName(gUesrName);
Map[] permissionMap = userService.getUserPermission

(Constant.USER_GROUP_GUEST);//public static final int USER_GROUP_GUEST = 1;
us.setUserPermissionArray(permissionMap);
return us;
}
*/
       us.setLastActiveTime

(System.currentTimeMillis());
       us.setValidateCode

(uo.getValidateCode());
       ac.getSession().put

(Constant.USER_SESSION_KEY, us);

       uc.removeAuthCode();//不用验证了!
       uc.addGuestCookies();

}
好的,我们看另外一个interceptor:userPermissionInterceptor,这个是用户权限的拦截器:
这里有 String actionName = "/" + ac.getName();// ac.getName()=nag!-->/nag!
String ajax = "html";
String saction = "";
Map map = ac.getParameters();
String[] _ajax = (String[]) map.get("ajax");//ajax参数!
if (_ajax != null) {
   ajax = _ajax[0];
}
String[] _saction = (String[]) map.get("action");//action参数!
if (_saction != null) {
   saction = _saction[0];
}
我们重点看下面的代码:
UserSession us = (UserSession) ac.getSession().get(Constant.USER_SESSION_KEY);//获得session

   Permission permission = (Permission) us.getUserPermission().get

(actionName + "?action=*");//我们知道加入的时间用的是setUserPermissionArray
/**
private Map userPermission = new HashMap();
public void setUserPermissionArray(Map[] permissionMap) {
setSpecialPermission(permissionMap[1]);
Set pset = permissionMap[0].entrySet();
Iterator it = pset.iterator();
while (it.hasNext()) {
   Map.Entry p = (Map.Entry) it.next();
   Permission permission = (Permission) p.getValue();
   String[] actions = permission.getAction().split(",");
   for (int i = 0; i < actions.length; i++) {
    String[] resources = ((String) p.getKey()).split(",");
    this.getUserPermission().put(resources[0] + "?action=" +

actions[i], p.getValue());
   }
}

}

*/
   if (permission != null) {
    havePermission = true;
   } else {
    permission = (Permission) us.getUserPermission().get

(actionName + "?action=" + saction);//带有saction!
    if (permission != null) {
     havePermission = true;
    } else {
     havePermission = false;
    }
   }
   if (havePermission) {
    return invocation.invoke();
   } else { //没有权限的话

    HttpServletRequest request = (HttpServletRequest) ac.get

(ServletActionContext.HTTP_REQUEST);
    StringBuffer sb = new StringBuffer();
    sb.append(BBSCSUtil.getWebRealPath(request));
    sb.append(request.getContextPath());
    sb.append("/");
    sb.append(BBSCSUtil.getActionMappingURLWithoutPrefix

(ac.getName())); sb生成一个完整的URL
    UrlHelper.buildParametersString(map, sb, "&");

    String curl = sb.toString();//含参数的完整的URL

    SysConfig sysConfig = (SysConfig) wc.getBean("sysConfig");

    ResourceBundleMessageSource messageSource =

(ResourceBundleMessageSource) wc.getBean("messageSource");

    if (ajax.equalsIgnoreCase("html")) {
     String errorMsg = messageSource.getMessage

("error.noPermission", null, ac.getLocale());
     ac.getValueStack().set("interceptError", errorMsg);
     ac.getValueStack().set("tourl", curl);//保存当前URL
     ac.getValueStack().set("useAuthCode",

sysConfig.isUseAuthCode());
     if (sysConfig.getUsePass() == 0) {
      return "intercepthtml";//不使用通行!
     } else {
      ac.getValueStack().set("actionUrl",

sysConfig.getPassUrl());//进入PASS的URL!
      return "intercepthtmlpass";
     }
    } else if (ajax.equalsIgnoreCase("shtml")) {
     String errorMsg = messageSource.getMessage

("error.noPermission", null, ac.getLocale());
     ac.getValueStack().set("interceptError", errorMsg);
     return "interceptshtml";
    } else {
     String errorMsg = messageSource.getMessage

("error.noPermission.ajax", null, ac.getLocale());
     AjaxMessagesJson ajaxMessagesJson =

(AjaxMessagesJson) wc.getBean("ajaxMessagesJson");
     ajaxMessagesJson.setMessage("E_NO_Permission",

errorMsg);
     ac.getValueStack().set("ajaxMessagesJson",

ajaxMessagesJson);
     return "ajaxjson";
    }
   }
/**
<global-results>
   <result name="error">/WEB-INF/jsp/error.jsp</result>
   <result name="htmlError">/WEB-INF/jsp/htmlError.jsp</result>
   <result name="ajaxjson">/WEB-INF/jsp/ajaxjson.jsp</result>
   <result name="jsonstring">/WEB-INF/jsp/jsonstring.jsp</result>
   <result name="intercepthtml">/WEB-

INF/jsp/intercepthtml.jsp</result>//用到
   <result name="intercepthtmlpass">/WEB-

INF/jsp/intercepthtmlpass.jsp</result>//用到
   <result name="interceptshtml">/WEB-

INF/jsp/interceptshtml.jsp</result>//用到
   <result name="relogin" type="redirect-action">login?

action=relogin</result>
   <result name="boardPasswd">/WEB-INF/jsp/boardPasswd.jsp</result>
</global-results>
*/
我们可以看看具体的JSP页面,其中html是有登录的,而sthml是另外一种类型.它们都是ajax请求时,在

url上带的参数,用于区分返回的页面类型。
<%@page contentType="text/html; charset=UTF-8"%>
<%@taglib uri="/WEB-INF/struts-tags.tld" prefix="s"%>
<div id="error" class="errormsg">
<s:property value="%{interceptError}"/>
</div>
我们看看资源文件中的内容:
error.noPermission=您没有操作此项功能的权限,如果您还没有登录,可能需要登录才能操作!<a

href="javascript:;" οnclick="Element.toggle('loginform');"><strong>我要登录</strong></a>
error.noPermission.ajax=您没有操作此项功能的权限!
注意:Element.toggle是prototype.js中的函数,交替隐藏或显示,见资料:

http://blog.sina.com.cn/u/562f0574010009pk
好的,我们将两个复杂的interceptor分析完了!我们还是回到Nag.java中吧:
它负责导航菜单的显示,extends BaseAction implements UserSessionAware,我们直接看execute方法


public String execute() {
int isHidden = 0;
if (userSession.isHaveSpecialPermission

(Constant.SPERMISSION_CAN_SEE_HIDDEN_BOARD)) { // 如果用户有查看隐藏版区的权限public static

final long SPERMISSION_CAN_SEE_HIDDEN_BOARD = 901;
/**
public boolean isHaveSpecialPermission(long permissionID) {
return this.specialPermission.containsKey(new Long(permissionID));
}
*/
   isHidden = -1;
}
this.setUrlRewrite(Constant.USE_URL_REWRITE);
this.boardList = this.getBoardService().findBoardsByParentID(0, 1, isHidden,

Constant.FIND_BOARDS_BY_ORDER);//顶层的!
// System.out.println(boardList);

for (int i = 0; i < this.boardList.size(); i++) {
   Board b = (Board) this.boardList.get(i);
   List bclist = this.getBoardService().findBoardsByParentID(b.getId(),

1, isHidden,
     Constant.FIND_BOARDS_BY_ORDER);
   this.boardMap.put(b.getId(), bclist);//2级的放入boardMap
}

List bsaveids = this.getBoardSaveService().findBoardSaveBidsByUid

(userSession.getId());userSession.getId()-->userId
this.boardSaveList = this.getBoardService().findBoardsInIDs(bsaveids);//用户

保存的版区!

this.setUsePass(this.getSysConfig().isUsePass());

return SUCCESS;
}
我们看jsp页面:
<!--
function loadChild(bid,btype) {
var obj = document.getElementById("child" + bid);
var imgObj = document.getElementById("img" + bid);
if (obj.style.display == "none") {
    obj.style.display = "block";
    imgObj.src="images/collapse.gif";
}
else {
    obj.style.display = "none";
    imgObj.src="images/expand.gif";
}
}

function loadBoardSave() {

var obj = document.getElementById("boardSaveDiv");
var imgObj = document.getElementById("boardSaveImg");
if (obj.style.display == "none") {
    obj.style.display = "block";
    imgObj.src="images/collapse.gif";
}
else {
    obj.style.display = "none";
    imgObj.src="images/expand.gif";
}
}

function loadUserCenter() {
var obj = document.getElementById("userCenterDiv");
var imgObj = document.getElementById("imgUserCenterSet");
if (obj.style.display == "none") {
    obj.style.display = "block";
    imgObj.src="images/collapse.gif";
}
else {
    obj.style.display = "none";
    imgObj.src="images/expand.gif";
}
}

function loadUserLogout() { //不用了
var obj = document.getElementById("userLogoutDiv");
var imgObj = document.getElementById("imgLogout");
if (obj.style.display == "none") {
    obj.style.display = "block";
    imgObj.src="images/collapse.gif";
}
else {
    obj.style.display = "none";
    imgObj.src="images/expand.gif";
}
}
//-->
</script>
<base target="mainFrame"/>
body区域分<div> <ul><li>第一项</li>下面是用户中心的li:
<li><a href="javascript:;" οnclick="loadUserCenter();"><img id="imgUserCenterSet"

src="images/expand.gif" alt="" width="25" height="15" border="0" align="absmiddle"/><s:text

name="nag.usercenter"/></a></li>
而其下的div:
<div id="userCenterDiv" class="nag" style="display:none">
        <ul>
          <li>
          <s:url action="signSet" id="signSetUrl"></s:url>
          <a href="${signSetUrl}"><img id="imgSignSet" src="images/node.gif" alt=""

border="0" align="absmiddle"/><s:text name="signset.title"/></a>
          </li>
          <li>
          <s:url action="nickNameSet" id="nickNameSetUrl"></s:url>
          <a href="${nickNameSetUrl}"><img id="imgNickNameSet" src="images/node.gif" alt=""

border="0" align="absmiddle"/><s:text name="nickset.title"/></a>
          </li>
          <s:url action="userConfig" id="userConfigUrl"></s:url>
          <li><a href="${userConfigUrl}"><img id="imgUserConfig" src="images/node.gif"

alt="" border="0" align="absmiddle"/><s:text name="userconfig.title"/></a></li>
          <s:url action="friendSet" id="friendSetUrl"></s:url>
          <li><a href="${friendSetUrl}"><img id="imgFriendSet" src="images/node.gif" alt=""

border="0" align="absmiddle"/><s:text name="friend.title"/></a></li>
          <s:url action="note" id="noteUrl"></s:url>
          <li><a href="${noteUrl}"><img id="imgNote" src="images/node.gif" alt="" border="0"

align="absmiddle"/><s:text name="note.title"/></a></li>
          <s:url action="bookMark" id="bookMarkUrl"></s:url>
          <li><a href="${bookMarkUrl}"><img id="imgBookMark" src="images/node.gif" alt=""

border="0" align="absmiddle"/><s:text name="bookmark.title"/></a></li>
          <s:url action="userFace?action=index" id="userFaceUrl"></s:url>
          <li><a href="${userFaceUrl}"><img id="imgFace" src="images/node.gif" alt=""

border="0" align="absmiddle"/><s:text name="face.title"/></a></li>
          <s:url action="userDetailSet?action=index" id="userDetailSetUrl"></s:url>
          <li><a href="${userDetailSetUrl}"><img id="imgUserDetailSet" src="images/node.gif"

alt="" border="0" align="absmiddle"/><s:text name="userdetail.title"/></a></li>
          <s:url action="cpasswd?action=index" id="cpasswdUrl"></s:url>
          <li><a href="${cpasswdUrl}"><img id="imgCpasswd" src="images/node.gif" alt=""

border="0" align="absmiddle"/><s:text name="cpasswd.title"/></a></li>
          <s:url action="boardSaveManage" id="boardSaveManageUrl"></s:url>
          <li><a href="${boardSaveManageUrl}"><img id="imgboardSaveManage"

src="images/node.gif" alt="" border="0" align="absmiddle"/><s:text

name="boardsave.title"/></a></li>
        </ul>
      </div>
</url>
</div>//上面的部分完成显示!

转载自原文链接, 如需删除请联系管理员。

原文链接:对天乙社区bbscs8实现的详细分析十五,转载请注明来源!

0