session过期和session同步(springmvc+mybatis+cookie)

1. session同步的需求:

     比如你的代码部署了机器A和机器B,这个时候你改了代码,想重新部署,并且不让用户发觉,也就是不影响正在使用你的系统的用户正常使用

session过期的需求:

用户登录后,很长时间并未进行任何操作,应提示登录过期,否则用户看到的是未更新的数据等问题(一般过期时间2小时)


2. 应用场景:

   (1)用户正在访问的是机器A,填写了一个申请单或者什么别的操作 

   (2)这个时候你部署机器A,导致用户没法继续访问A了,那么用户就去访问B,  这个时候用户和机器A的Session断了。

     (3)   为了不影响用户正常使用,你需要把用户和机器A的session,在用户一登录的时候,就保存到cookie一份,保存到redis一份。

            在用户和机器A断了session,并和B连接重新建立session的时候,你需要写代码去cookie里取一下sessionid,

           然后根据这个sessionid去redis里查一下,查到的数据重新赋值给 用户和机器B建立的session,

           这样你部署机器A的时候,正在访问系统的用户,之前的session得到延续,就不用重新登录系统了

         (我自己的情况:部署的nginx,设置为用户请求默认访问A,A挂了的情况下访问机器B) 


3. 实现:

     (1)   logincontroller代码中,也就是用户的登录代码中,用户的用户名和密码一验证完成,接着加入代码:

                request.setAttribute(SESSION_INFO, sessionInfo);
                request.getSession().setAttribute(SESSION_INFO,sessionInfo);
//放到session里便于jsp页面里取用户信息

                CookieHelper.setCookie(response, COOKIE_ID, encodeStrByBase64(SessionId));
                redisCacheUtil.setObject(SessionId, sessionInfo);

                                           其中的SessionId是登录完毕后,request.getSession().getId() 获取的

                                            CookieHelper是自己写的cookie相关的方法,setCookie里用的是javax.servlet.cookie

    public static void setCookie(HttpServletResponse response, String name,
            String value, int maxAge) {
        if (value == null)
            value = "";
        Cookie cookie = new Cookie(name, value);
        if (maxAge != 0) {
            cookie.setMaxAge(maxAge);
        } else {
            cookie.setMaxAge(3600);  //一小时的有效时间,这里可根据自己的需要设置不同的时间长度
        }
        cookie.setPath("/");
        response.addCookie(cookie);
    }

                                        encodeByBase64方法是加密方法,根据自己的需求选择不同的加密方法,比如md5啊,base64啊

                                         redisCacheUtil是redis管理的方法:

 /**
     * 将对象存入redis中
     * @param key-对象在redis里对应的key
     * @param obj-对象在redis里对应key的值
     * @return
     */
    public String setObject(final String key,final Object obj) {
        return new Executor<String>(jedisPool) {
            @Override
            String execute() {
                String result = jedis.set(key.getBytes(), SerializeUtil.serialize(obj));
                jedis.expire(key.getBytes(), 3600);
                return result;
            }
        }.getResult();
    }

         (2)  springmvc.XML文件中配置拦截(对大部分请求,都需要拦截看session是否有效,登录或者退出这种url就没必要拦截判断了):

<!-- 拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <bean class="com.xx.interceptors.SecurityInterceptor">
                <!-- 不需要权限验证的地址 -->
                <property name="excludeUrls">
                    <list>
                        <value>/</value>                                        <!-- 用户登录页面 -->
                        <value>/login</value>                                    <!-- 用户登录 -->
                        <value>/register</value>                                <!-- 用户注册 -->
                        <value>/console</value>                        
                        <value>/index</value>                        
                        <value>/logout</value>                       
                    </list>
                </property>
                <!-- 引入spring-redis.xml里定义的bean -->
                <property name="redisCacheUtil">
                    <ref bean="redisCacheUtil"/>
                </property>
            </bean>
        </mvc:interceptor>
    </mvc:interceptors>

             

         (3)在web.xml中配置session过期时间

<!-- 配置session超时时间1小时,单位分钟 . -->
    <session-config>
        <session-timeout>60</session-timeout>
    </session-config>

          (4)拦截后的url,在springmvc.xml中配置的SecurityInterceptor类的 preHandle方法,是拦截到的url必须要执行的代码,

                  在这里,判断用户的session是否过期或者session同步的功能支持

                SessionInfo info = (SessionInfo) request.getSession().getAttribute(SESSION_INFO);
            
            if(info != null) {
                request.setAttribute(SESSION_INFO, info);//为了不改代码里面的大部分内容
                //还得不停的刷一下redis,不然用户连续操作三个小时的情况下,由于redis两小时过期,会导致部署代码时导致的session中断,用户感知到
                String SessionId=request.getSession().getId();
                if(StringUtils.isNotBlank(SessionId)){
                    redisCacheUtil.setObject(SessionId, info);
                }
                return true;    
            }else {          
                
                //一旦检测到用户和服务器的服务中断(服务部署代码中断或者用户session过期中断),也就是request请求中的SessionInfo为空,
                //那么先去用户浏览器的cookie中拿sessionid,
                //然后根据sessionid去redis拿数据,
                //如果redis的也过期了,那就说明是session正常过期
                //如果redis有,说明是部署A机器的时候,session中断,那么这时候切换到B机器,session可以在B机器上继续使用
                String cookieValue = CookieHelper.getCookieValue(request, COOKIE_ID);
                String userCookieValue = decodeByBase64(cookieValue);//对cookie进行解码
                Object obj = redisCacheUtil.getObject(userCookieValue);

                if(obj!=null){
                    logger.error("正在部署机器,用户此时应该能session共享!");
                    SessionInfo sessionInfo=(SessionInfo) obj;
                    request.setAttribute(SESSION_INFO, sessionInfo);
                    request.getSession().setAttribute(Constant.SESSION_INFO,sessionInfo);//放到session里便于jsp页面里取用户信息
                    return true;
                }else{
                    logger.error("您还没有登录或登录已超时,请重新登录,然后再刷新本功能!");
                    StringBuilder jsonStr = new StringBuilder();
                    jsonStr.append("{");
                    jsonStr.append("\"code\":\"test_login\"");
                    jsonStr.append(",\"ssoUrl\":\""+ConstantUtil.get("session_url") + "/error/logon.jsp\"");
                    jsonStr.append("}");
                    response.setHeader("Content-type", "text/html;charset=UTF-8");
                    response.setCharacterEncoding("UTF-8");
                    response.setStatus(403);
                    response.getWriter().write(jsonStr.toString());
                    //logger.error("test sessiontimeout url="+jsonStr.toString());
                    return false;
                }

猜你喜欢

转载自blog.csdn.net/daiqinge/article/details/76636521
今日推荐