IpuWadeMobile的Session管理

Session管理

一、场景描述:

在你已经清晰的了解了不进行Session管理的环境下,进行普通页面的开发以及进行页面之间的跳转的前提下,你或许会考虑让你的服务器进行Session管理,以控制关键页面的访问和关键数据接口的调用。

二、具体需求:

使用Session进行会话管理,进行Session验证。

三、解决方案:

A、实现思路:

首先想到,需要一个自定义的ContextData(上下文数据),用来缓存会话中产生的业务数据。再需要一个自定义的SessionManager(会话管理者),来进行自定义的Session校验并管理ContextData。在自定义Session校验的逻辑中,可以使用Utility.error(paramString)或error(paramString)来抛出会话异常。当Session异常时,怎么办呢?此时便需要一个自定义的ExceptionHandler(异常管理者),来处理Session异常。在ExceptionHandler中,可以使用MobileServerException.class.isInstance(e)和((MobileServerException)e).getCode()=="-100"来判断是否为Session异常,并作出相应处理。在登陆时,我们可以使用IpuSessionManager.getInstance().createSession(contextData)来创建Session。此方法会返回一个SessionId,我们应将这个SessionId返回给前端JS。前端JS获取到此SessionId后,保存到内存中。通过在common.js中重写核心API(如openPage,callSvc),让前端在访问需要进行Session校验的页面时,自动在传入参数中携带好本次会话的SessionId。最后,别忘了在server-config.xml中配置好你的异常处理类和会话管理类(配置参数为exceptionHandler和sessionManager)。

B、具体实现:

1.自定义上下文数据,类名IpuContextData。如下,在上下文数据中存入了当前登陆用户的用户名。

package com.ipu.server.core.context;
import com.ailk.mobile.frame.context.impl.DefaultContextData;
public class IpuContextData extends DefaultContextData {
    private static final long serialVersionUID = 1L;
    public String getUserName(){
        return contextData.getString("USER_NAME");
    }
    public void setUserName(String name){
        contextData.put("USER_NAME", name);
    }
}

2.自定义会话管理者,类名IpuSessionManager。如下,用来书写Session校验逻辑,进行登陆状态的检测。由于下面进行了用户名的校验,故需要在每次请求时,除了携带SessionId外,还需要携带当前登陆用户的用户名,并且要求保证本次请求传入的用户名与Context中保存的用户名(登陆时传入的用户名)一致。

package com.ipu.server.core.session;
import com.ailk.common.data.IData;
import com.ailk.common.util.Utility;
import com.ailk.mobile.frame.context.IContextData;
import com.ailk.mobile.frame.session.impl.AbstractSessionManager;
import com.ipu.server.core.context.IpuContextData;
public class IpuSessionManager extends AbstractSessionManager {
    @Override
    public void customVerify(String paramString, IData paramIData,
            IContextData paramIContextData) throws Exception {
        System.out.println("IpuSessionManager.customVerify()");
        String userName = paramIData.getString("USER_NAME");
        String contextUserName = ((IpuContextData)paramIContextData).getUserName();
        if(userName == null || !userName.equals(contextUserName)){
            Utility.error("非法操作,请重新登陆!");
        }
    }
}

3.自定义异常管理类,类名IpuExceptionHandler。如下,用来处理Session校验异常。此处需要注意,当在浏览器上访问时,ServletManager.openPage("SessionErr")会生效,当客户端访问时,由于新页面并非来自服务器端,上面的页面跳转不会生效。所以更明智的做法是,在此处书写其它业务逻辑,而是否进行页面跳转的判断在js端去执行。

package com.ipu.server.core.handle;
import com.ailk.mobile.frame.handle.impl.DefaultExceptionHandler;
import com.ailk.mobile.servlet.ServletManager;
import com.ailk.mobile.util.MobileServerException;
public class IpuExceptionHandler extends DefaultExceptionHandler {
    @Override
    public void pageErrorHandle(Exception e, String pageAction, String data)
            throws Exception {
        if(MobileServerException.class.isInstance(e)){
            if(((MobileServerException)e).getCode()=="-100"){
                ServletManager.openPage("SessionErr");
            }
        }
        super.pageErrorHandle(e, pageAction, data); //执行父类的逻辑
    }
}

4.自定义数据接口的父类,类名IpuAppBean。如下,由于一般的数据接口类都是继承自AbstractBean,他的getContextData方法返回的数据类型为IContext,为了避免每次获取时主动的强制类型转换,可以自定义一个数据接口的父类,将强制类型转换的逻辑写入父类中。

package com.ipu.server.core.bean;
import com.ailk.mobile.frame.bean.AbstractBean;
import com.ipu.server.core.context.IpuContextData;
public class IpuAppBean extends AbstractBean {
    @Override
    protected IpuContextData getContextData() throws Exception {
        return (IpuContextData)(getContext().getContextData());
    }
}

5.创建登陆验证的数据接口,类名Login,方法名checkLogin。如下,从前端传来用户名和密码之后,可以进行用户名和密码的校验,如若校验通过,则首先创建一个新的上下文数据,作为创建Session时的参数,进而创建了一个新的Session,同时,将新的Session对应的SessionId返回给JS端。

package com.ipu.server.bean;
import com.ailk.common.data.IData;
import com.ailk.common.data.impl.DataMap;
import com.ipu.server.core.bean.IpuAppBean;
import com.ipu.server.core.context.IpuContextData;
import com.ipu.server.core.session.IpuSessionManager;
public class Login extends IpuAppBean{
    public IData checkLogin(IData param){
        String userName = param.getString("USER_NAME");
        @SuppressWarnings("unused")
        String passWord = param.getString("PASS_WORD");
        if(userName == null){
            userName = "无名氏";
        }
        String sessionId="ERR";
        try {
            IpuContextData contextData = new IpuContextData();
            contextData.setUserName(userName);
            sessionId = IpuSessionManager.getInstance().createSession(contextData);
        } catch (Exception e) {
            e.printStackTrace();
        }
        IData resultData = new DataMap();
        resultData.put("SESSION_ID",sessionId);
        return resultData;
    }
}

6.配置Login.checkLogin数据接口。找到etc/server-data.xml配置文件,在datas节点下,添加或修改如下配置。

<action name="Login.checkLogin" class="com.ipu.server.bean.Login" method="checkLogin" verify="false" >
</action>

7.配置会话管理类和异常管理类。找到etc/server-config.xml配置文件,添加或修改如下两项配置(exceptionHandler和sessionManager)。

<config name="exceptionHandler" value="com.ipu.server.core.handle.IpuExceptionHandler"/>
<config name="sessionManager" value="com.ipu.server.core.session.IpuSessionManager"/>

8.创建自己的登陆页面,文件名Login.html。

<!DOCTYPE html>
<html>
  <head>
    <title>登录</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <link rel="apple-touch-icon" href="touch-icon-iphone.png" />
    <link rel="apple-touch-icon-precomposed" href="touch-icon-iphone.png" />
    <!--1,引入CSS文件 -->
    <link rel="stylesheet" href="biz/ipu/theme/hum.css">
  </head>
  <body>

  <header class="ui-toolbar" id="header" >
    <a class="ui-icon icon-left-nav fn-left" href="javascript:;" action="Index" ></a>
    <h1 class="ui-toolbar-title">登录</h1>
  </header>
  <div class="ui-panel-content">
      <form>
          <input type="text" placeholder="用户名" id="userName" />                
          <input type="text" placeholder="密码" id="passWord"  />
          <button type="button" class="ui-btn ui-btn-block" id="loginBtn" >登录</button>
      </form>
  </div>
  </body>
      {%>template/common/Head.html%}
    <script type="text/javascript" src="biz/js/Login.js"></script>
</html>

9.配置自己的登陆页面。找到etc/server-page配置文件,在pages节点下,添加或修改如下配置项。

<action name="Login" template="template/webapp/ipu/Login.html" ></action>

10.书写业务JS逻辑,文件名Login.js。如下,通过Mobile的callSvc方法,访问数据接口Login.checkLogin。在回调方法中,获取到返回的SessionId,并调用Mobile的setMemoryCache方法,将SessionId和UserName存入内存中(以便后续需要时取用,{注:在common.js中需要})。

require(["mobile","zepto","jcl"],function(Mobile,$,Wade){

    $("#header").children().click(function(){

        Mobile.openPage("Index");

    });

    $("#loginBtn").click(function(){

        Mobile.loadingStart("登陆中,请稍等……","等待");

        setTimeout(function(){

            Mobile.loadingStop();

            var userName = $("#userName").val();

            var passWord = $("#passWord").val();

            var data = Wade.DataMap();

            data.put("USER_NAME",userName);

            data.put("PASS_WORD",passWord);

            Mobile.callSvc("Login.checkLogin",data,function(resultData){

                var myData = new Wade.DataMap(resultData);

                var sessionId = myData.get("SESSION_ID");

                if(sessionId){

                    Mobile.setMemoryCache("SESSION_ID",sessionId);

                    Mobile.setMemoryCache("USER_NAME",userName);

                    Mobile.tip("登陆成功!");

                    Mobile.openPage("Index");

                }else{

                    alert("登陆异常,请联系管理员!");

                }

            });

        },600);

    });

});

11.在web/biz/js/common/common.js中,重写核心Api(如:openPage,callSvc)。如下,从内存中获取到之前存入的SessionId和UserName,将这两个数据合并到即将发往服务端的参数中。即,通过common.js中的方法发送数据请求时,会自动带上SessionId和UserName这两个参数。

this.openPage = function(action,param){

    param = param?param:new Wade.DataMap();

    Mobile.getMemoryCache(function(data){

        data = new Wade.DataMap(data);

        param.put("SESSION_ID",data.get("SESSION_ID"));

        param.put("USER_NAME",data.get("USER_NAME"));

        Mobile.openPage(action,param);

    },[‘SESSION_ID‘,‘USER_NAME‘]);

};

this.callSvc = function(action,param,callback,err){

    param = param?param:new Wade.DataMap();

    Mobile.getMemoryCache(function(data){

        data = new Wade.DataMap(data);

        param.put("SESSION_ID",data.get("SESSION_ID"));

        param.put("USER_NAME",data.get("USER_NAME"));

        Mobile.callSvc(action,param,callback,err);

    },[‘SESSION_ID‘,‘USER_NAME‘]);

};

12.创建一个普通的html页面,文件名Test.html。如下,此页面会将获取到的UserName显示到页面上。注意,此UserName是从上下文数据中获取到,并返回到页面上的。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "
http://www.w3.org/TR/html4/loose.dtd
">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>Insert title here</title>

{%>template/common/Head.html%}

</head>

<body>

    <h1>

        Session数据:{%USER_NAME%}

    </h1>

    <input type="button" value="打开照相" id="getPhoto" >

    <script type="text/javascript" src="biz/js/Test.js"></script>

</body>

</html>

13.创建一个普通的数据接口,类名Test,方法名getData。如下,他继承自自定义的IpuAppBean,并调用了父类中的getContextData方法来获取上下文数据。

package com.ipu.server.bean;
import com.ailk.common.data.IData;
import com.ailk.common.data.impl.DataMap;
import com.ipu.server.core.bean.IpuAppBean;
import com.ipu.server.core.context.IpuContextData;
public class Test extends IpuAppBean {
    public IData getData(IData param) throws Exception{
        IData data = new DataMap();
        IpuContextData contextData = getContextData();
        String name = contextData.getUserName();
        data.put("USER_NAME", name);
        return data;
    }
}

14.书写普通html页面对应的JS业务逻辑,文件名Test.js。

require(["mobile","zepto","wadeMobile"],function(Mobile,$,WadeMobile){

    $("#getPhoto").click(function(){

        Mobile.tip("测试成功!!!");

        WadeMobile.getPhoto(function(result){

            alert(result);

        },1);

    });

});

15.配置普通的数据接口。找到etc/server-data.xml配置文件,在datas节点下,添加或修改如下配置项。注意,此处的verify="true"。说明,访问这个数据接口时,会进行Session校验。

<action name="Test.getData" class="com.ipu.server.bean.Test" method="getData" verify="true" >
</action>

16.配置普通的html页面。找到etc/server-page.xml配置文件,在pages节点下,添加或修改如下配置项。

<action name="Test" template="template/webapp/ipu/Test.html" data="Test.getData" >

</action>

17.关键点。上述工作完成后,如果需要从某个页面(如index.html页面),跳转到需要进行Session校验的页面(或仅仅是访问需要进行Session校验的数据接口),应当(也必须)使用common.js中重写的核心Api(如,Common.openPage,Common.callSvc),否则会产生Session异常。原因是,使用普通的打开新页面的方法(如:Mobile.openPage,Mobile.callSvc),不会自动将SessionId(以及UserName)带到服务器端,故服务器端的Session校验无法通过。写法,请参看如下示例。

require(["mobile","jquery","common"],function(Mobile,$,Common){

    $("#openTestPage").click(function(){

        Common.openPage("Test");

    });

});
时间: 2024-10-06 07:01:49

IpuWadeMobile的Session管理的相关文章

转:通过Spring Session实现新一代的Session管理

长期以来,session管理就是企业级Java中的一部分,以致于我们潜意识就认为它是已经解决的问题,在最近的记忆中,我们没有看到这个领域有很大的革新. 但是,现代的趋势是微服务以及可水平扩展的原生云应用(cloud native application),它们会挑战过去20多年来我们设计和构建session管理器时的前提假设,并且暴露了现代化session管理器的不足. 本文将会阐述最近发布的Spring Session API如何帮助我们克服眼下session管理方式中的一些不足,在企业级Ja

JSP Session管理

阅读目录 先说说cookie与session session常用的方法 session声明周期 管理session过期 通过tomcat监控管理session 在Eclipse重启Tomcat,tomcat-users.xml被重置 本篇讲述JSP中session的相关知识和管理方法: 回到顶部 先说说cookie与session 在web中常用的两种用户信息管理方式:cookie 和 session. cookie是保存在用户客户端的数据,用于避免每次发送http请求时,连带过多的数据,造成复

基于DDD的现代ASP.NET开发框架--ABP系列之7、ABP Session管理

点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之7.ABP Session管理 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ABP的官方网站:http://www.aspnetboilerplate.com ABP在Github上的开源项目:https://github.com/aspnetboilerplate 简介 如果一个应用程序需要登录,则它必须知道当前用户执行了什么操作.因此ASP.NET

008-shiro与spring web项目整合【二】认证、授权、session管理

一.认证 1.添加凭证匹配器 添加凭证匹配器实现md5加密校验. 修改applicationContext-shiro.xml: <!-- realm --> <bean id="customRealm" class="com.lhx.ssm.shiro.CustomRealm"> <!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 --> <property name="creden

How Tomcat works — 八、tomcat中的session管理

在使用shiro的session的时候感觉对于tomcat中session的管理还不是特别清楚,而且session管理作为tomcat中比较重要的一部分还是很有必要学习的. 目录 概述 session的作用 session新建.查找和更新 session删除 总结 概述 在tomcatsession管理的类标准实现为StandardManager,主要作用为启动的是加载缓存的session,类关系如下: 在用户servlet中使用的session为StandardSessionFacade(也是

Session管理之ThreadLocal

在各种Session 管理方案中, ThreadLocal 模式得到了大量使用.ThreadLocal 是 Java中一种较为特殊的线程绑定机制.通过ThreadLocal存取的数据,总是与当前线程相关. 也就是说,JVM 为每一个执行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出 现的并发訪问问题提供了一种隔离机制. 首先,我们须要知道.SessionFactory负责创建Session,SessionFactory是线程 安全的,多个并发线程能够同一时候訪问一个SessionFa

Tomcat源码分析——Session管理分析(上)

前言 对于广大java开发者而已,对于J2EE规范中的Session应该并不陌生,我们可以使用Session管理用户的会话信息,最常见的就是拿Session用来存放用户登录.身份.权限及状态等信息.对于使用Tomcat作为Web容器的大部分开发人员而言,Tomcat是如何实现Session标记用户和管理Session信息的呢? 概要 Session Tomcat内部定义了Session和HttpSession这两个会话相关的接口,其类继承体系如图1所示. 图1 Session类继承体系 图1中额

【转】UniGUI Session管理說明

[转]UniGUI Session管理說明 (2015-12-29 15:41:15) 转载▼   分类: uniGUI 台中cmj朋友在uniGUI中文社区QQ群里发布的,转贴至此. UniGUI Session管理說明每一個Session對應一個UniMainModule,一個MainForm因此Session+UniMainModule就可以得到所有Session+使用者的資料以做管理之用 [UniServerModule]   Public區定義    UserList:TList; /

【摘自大型网站技术架构书】应用服务器集群的session管理

由于负载均衡服务器可能会将请求分发到集群任何一台服务器上,所以保证每次请求能够获得正确的session比单机时复杂. 集群环境下,session管理的主要几种手段 1.session复制 session复制是早期的企业级的使用比较多的一种服务器集群session管理机制.应用服务器开启web容器的session复制功能,在集群中的几台服务器之间同步session对象,使得每台服务器上都保存所有的session信息,这样任何一台宕机都不会导致session的数据丢失,服务器使用session时,直