会话Session处理

介绍:

Session,又被称为会话。是指有始有终的一系列动作/消息。

用户请求访问某个网站域名时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象,存放在服务端,此对象的唯一标识放入cookie中。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。但是session对象是有生命周期的,当会话过期或被放弃后,服务器将终止该会话。

Session 对象存储特定用户会话所需的属性及配置信息。最常见的一个用法就是存储用户的首选项。例如,用户登录之后的登录信息,就可以将该信息存储在 Session 对象中。

在开发工程中,常用到的是javax.servlet.http.HttpSession。

cookie与session:

我们都知道,HTTP协议本身是无状态的,客户端只需要简单的向服务器发送请求,无论是客户端还是服务器都没有必要纪录彼此过去的行为,每一次请求之间都是独立的。

但是在后来发展应用中,需要客户端和服务端保持状态。这样cookie就产生了,其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机制则是又一种在客户端与服务器之间保持状态的解决方案。cookie就是在客户端保持状态,session是在服务端保持状态,但是又是借助客户端的,所以在使用过程中,session的唯一标识常保持在cookie中的。一般取名JSESSIONID=唯一标识(可以动过UUID方式产生)。考虑到实际情况中,cookie在客户端被禁用了,这时候可以直接通过请求参数方式传入。

因为session的唯一标识在cookie当中,跟随着cookie的生命周期。一般cookie的默认生命周期是浏览器关闭结束,所以session在浏览器关闭时也当做结束。但是,只要服务端session还在,通过相同的session唯一标示依然可以保持状态。

前面说过,session保持在服务端,当大量请求时,session就会占用大量内存,所以在会给session设置个过期时间,释放空间。

httpsession:

httpSession是java提供的一个接口。提供了一些对session的操作方法:

  • public String getId(); //获取session的唯一标识
  • public long getLastAccessedTime(); //获取最后的请求过来的时间(毫秒)
  • public ServletContext getServletContext();//获取session所属的上下文
  • public void setMaxInactiveInterval(int interval);//设置有效期(秒)
  • public Object getAttribute(String name); //获取session中存放的对象
  • public Enumeration getAttributeNames(); //获取所有存放对象
  • public void setAttribute(String name, Object value); //存放对象到session中。如果放入的对象为null,效果跟removeAttribute()一致。
  • public void removeAttribute(String name);//移出session中的对象
  • public void invalidate(); //无效session
  • public boolean isNew(); //判断客户端是不是支持session的。如果客户端不支持cookie,每次请求都会创建个新的session。

    一般情况下,session都是存储在内存里,当服务器进程被停止或者重启的时候,内存里的session也会被清空,如果设置了session的持久化特性,服务器就会把session保存到硬盘上,当服务器进程重新启动或这些信息将能够被再次使用。

文章开头,当请求过来时,没有就创建。其实严格的来说,并非这样的,实际上是调用了HttpServletRequest中的public HttpSession getSession(boolean create);实现时才获取出来session。
我们先来看下源码中这个方法的说明:

*返回此请求的关联当前httpSession,如果没有,当create设置为true时,就会创建个新的session;
如果create为false,同时请求request没有有效的httpSession,则就会返回null;*
我们可以这样测试:创建两个页面,一个是jsp,一个是html。jsp本质上就是一个servlet,参与服务交互,SP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。html是个静态页面,与服务器没有啥交互。

http://localhost/web_01/testSession.html 请求后,可以看出返回response中没有session。
这里写图片描述

http://localhost/web_01/welcome.jsp 请求后,可以看出有session了。
这里写图片描述

我们也是可以控制不创建session。在webcome.jsp页面上添加:
<%@ page session="false" %> 设置session为false时,关闭session。再次请求可以看到,没有看到session了。
这里写图片描述

我们可以深入源码中探查:
新创建个jsp页面error.jsp,这个session默认是打开的。请求welcome.jsp和error.jsp页面后,在tomcat中查看所生成的对应servlet文件。生成的servlet文件在tomcat下面的 work\Catalina\localhost\web_01\org\apache\jsp文件夹中。
首先看到error.jsp生成的servlet文件error_jsp.java:

package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class error_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent {
    private static final javax.servlet.jsp.JspFactory _jspxFactory =
          javax.servlet.jsp.JspFactory.getDefaultFactory();

    //其他代码省略
    **********

    public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
        throws java.io.IOException, javax.servlet.ServletException {

    final javax.servlet.jsp.PageContext pageContext;
    //注意:这里是HttpSession
    javax.servlet.http.HttpSession session = null;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;


    try {
      response.setContentType("text/html; charset=UTF-8");
      pageContext = _jspxFactory.getPageContext(this, request, response,
                null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      //从页面上下文中获取session
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

      //省略页面渲染代码
    } catch (java.lang.Throwable t) {
      //省略异常处理代码
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

代码中可以看到HttpSession是从PageContext中获取。注意,这里的pageContext所属package为javax.servlet.jsp下面,此所属jar在tomcat本身的lib文件jsp-api.jar中。
这个pageContext是个抽象类,代码中pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true);是个抽象类赋上具体的实现,再通过代码:
private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();
定位到JspFactory类。进入看下源代码:
//***********
先省略,后续补上。
//*********

当session关闭时,web.jsp的servlet文件中就没有看到HttpSession的相关内容。

当session被调用invalidate()方法时,或过期时就会终止。

分布式环境下的session:

因为session是存在服务器上,当应用集群时就成为一个问题。请求同一个网站,前一个请求到A服务器上,获取session,保持在A服务器上,但是下一个请求可能会分配到B服务器上,此时就识别不了session了。
一般来说有几种解决方法:


1:服务器直接同步session:集群中的服务器相互同步session,但是问题不少。首先实时性不好保证,其次同步的次数随着服务器的数量二指数级别增加,所以在实际中很少用到。
2:对请求进行筛选处理:判断请求的IP,给分配到固定的服务器上,达到同一个IP请求始终访问同一个服务器。但是依然问题不少:请求IP解析匹配的开销不少;如果某个服务器挂掉了,会导致访问这个请求失败;削弱了负载均衡的能力,会导致某些服务器负载很高,而某些却空闲;动态增减服务器需要修改ip的分配,这回增减很多难度。
3:使用缓存:让集群的session都放入同一个缓存中,与服务器脱离依赖。现实中常用这样的方式。不过这样缓存就会成为一个瓶颈,不过可以考虑对缓存进行集群来解决。


缓存session实例:

首先配置redis相关:

mvn包:

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.7.1.RELEASE</version>
</dependency>

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.8.1</version>
</dependency>

xml配置(使用的是spring-context-4.2.xsd):


<context:property-placeholder location="config/redis.properties"/>

<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 池中可借的最大数 -->
        <property name="maxTotal" value="50" />
        <!-- 允许池中空闲的最大连接数 -->
        <property name="maxIdle" value="10" />
        <!-- 允许池中空闲的最小连接数 -->
        <property name="minIdle" value="2" />
        <!-- 获取连接最大等待时间(毫秒) -->
        <property name="maxWaitMillis" value="12000" />
        <!-- 当maxActive到达最大数,获取连接时的操作  是否阻塞等待  -->
        <property name="blockWhenExhausted" value="true" />
        <!-- 在获取连接时,是否验证有效性 -->
        <property name="testOnBorrow" value="true" />
        <!-- 在归还连接时,是否验证有效性 -->
        <property name="testOnReturn" value="true" />
        <!-- 当连接空闲时,是否验证有效性 -->
        <property name="testWhileIdle" value="true" />
        <!-- 设定间隔没过多少毫秒进行一次后台连接清理的行动 -->
        <property name="timeBetweenEvictionRunsMillis" value="1800000" />
        <!-- 每次检查的连接数 -->
        <property name="numTestsPerEvictionRun" value="5" />
    </bean>

    <bean id="redisFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="poolConfig" ref="jedisPoolConfig"></property>
        <property name="hostName" value="${redis.host}"></property>
        <property name="port" value="${redis.port}"></property>
        <property name="timeout" value="${redis.timeout}"></property>
    </bean>

    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="redisFactory" />
        <property name="keySerializer">  
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />  
        </property> 
    </bean>

自定义session类:


import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.zcl.session.util.SessionManager;

/**
 * 自定义Session类
 *
 */
public class SystemSession implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 3596476624020390228L;
    /**
     * session数据存储map
     */
    private Map<String,Object> sessionData = new HashMap<String,Object>();
    /**
     * sessionId  session标识
     */
    private String sessionId = null;

    public String getSessionId() {
        return sessionId;
    }

    public void setSessionId(String sessionId) {
        this.sessionId = sessionId;
    }

    public Object getAttribute(String name){
        Object value = sessionData.get(name);
        return value;
    }

    public void setAttribute(String name,Object value){
        sessionData.put(name, value);
        SessionManager.updateSession(this.sessionId, this);
    }

    public void removeAttribute(String name){
        sessionData.remove(name);
        SessionManager.updateSession(this.sessionId, this);
    }

    public void removeAllAttribute(){
        sessionData.clear();
        SessionManager.updateSession(this.sessionId, this);
    }

    public void disable(){
        SessionManager.deleteSession(this.sessionId);
    }

    public boolean hasAttributeName(String name){
        return sessionData.containsKey(name);
    }

    public Set<String> getAttributeNames(){
        return sessionData.keySet();
    }

    public Map<String, Object> getSessionData() {
        return sessionData;
    }

    public void setSessionData(Map<String, Object> sessionData) {
        this.sessionData = sessionData;
    }
}

session管理类:

import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.data.redis.core.RedisTemplate;
import com.zcl.constants.Constants;
import com.zcl.session.bean.SystemSession;
import com.zcl.util.CookieUtil;
import com.zcl.util.SpringUtils;
import com.zcl.util.UUIDUtils;


/**
 * 自定义Session的工具类
 * 
 * 
 */
public class SessionManager {

    private static Logger logger = Logger.getLogger(SessionManager.class);

    /**
     * 从request中获取sessionKey的值
     * 
     * @Title: getSessionIdFromRequest
     * @Description: TODO
     * @param request
     * @param sessionKey
     * @return
     */
    public static String getSessionIdFromRequest(HttpServletRequest request, String sessionKey) {

        String sessionId = null;
        // 请求参数 中获取session_id
        if (StringUtils.isBlank(sessionId)) {
            sessionId = request.getParameter(sessionKey);
        }
        // 请求头 中获取session_id
        if (StringUtils.isBlank(sessionId)) {
            sessionId = request.getHeader(sessionKey);
        }
        // 从cookie中获取session_id
        Cookie cookie = CookieUtil.getCookieByName(request, sessionKey);
        if (cookie != null && StringUtils.isBlank(sessionId)) {
            sessionId = cookie.getValue();
        }
        // 从request参数中获取
        if (StringUtils.isBlank(sessionId))
            sessionId = (String) request.getAttribute(sessionKey + "_attr");

        return sessionId;
    }

    /**
     * 获取一个session
     * 
     * @param request
     * @param response
     * @return
     */
    public static SystemSession createSession(String sessionKey, String sessionId, HttpServletRequest request,
            HttpServletResponse response) {

        if (StringUtils.isBlank(sessionKey))
            return null;

        if (StringUtils.isBlank(sessionId))
            sessionId = UUIDUtils.generateSessionKey();

        /*
         * sessionId保持入cookie中
         */
        response.setHeader("P3P","CP='CP=CAO PSA OUR'");
        CookieUtil.addCookie(request, response, sessionKey, sessionId, null);

        //现实中考虑到这里创建session后,后续就要立即使用,之后放在request的attribute中的。
        request.setAttribute(sessionKey+"_attr", sessionId);

        /*
         * 新建session.保存入redis
         */
        SystemSession session = new SystemSession();
        session.setSessionId(sessionId);
        RedisTemplate<String, SystemSession> redisTemplate = getRedisTemplate();
        redisTemplate.opsForValue().set(sessionId, session);
        redisTemplate.expire(sessionId, Constants.APP_SESSION_TIMEOUT, TimeUnit.SECONDS);

        return session;
    }

    /**
     * 根据sessionId从redis中获取对应session信息
     * 
     * @Title: getSession
     * @Description: TODO
     * @param sessionId
     * @return
     */
    public static SystemSession getSessionFromRedis(String sessionId) {
        SystemSession session = null;

        if (StringUtils.isNotBlank(sessionId)) {
            RedisTemplate<String, SystemSession> redisTemplate = getRedisTemplate();
            session = (SystemSession) redisTemplate.opsForValue().get(sessionId);
        }

        return session;
    }

    /**
     * 通过uuid生成sessionID
     * 
     * @return
     */
    public static String generateSessionKey() {
        String sessionKey = UUID.randomUUID().toString();
        return sessionKey.replace("-", "");
    }

    /**
     * 更新session数据内容
     * 
     * @param sessionKey
     * @param session
     */
    public static void updateSession(String sessionKey, SystemSession session) {
        setSessionTimeout(sessionKey, session);
    }

    /**
     * 删除session,使其失效
     * 
     * @param sessionKey
     */
    public static void deleteSession(String sessionKey) {
        RedisTemplate<String, SystemSession> redisTemplate = getRedisTemplate();
        redisTemplate.delete(sessionKey);
    }

    public static RedisTemplate<String, SystemSession> getRedisTemplate() {
        @SuppressWarnings("unchecked")
        RedisTemplate<String, SystemSession> redisTemplate = (RedisTemplate<String, SystemSession>) SpringUtils
                .getBeanByName("redisTemplate");
        return redisTemplate;
    }

    /**
     * 设置Session超时时间
     * 
     * @return
     */
    private static void setSessionTimeout(String sessionKey, SystemSession session) {
        long sessionTimeout = Constants.APP_SESSION_TIMEOUT;
        RedisTemplate<String, SystemSession> redisTemplate = getRedisTemplate();
        redisTemplate.opsForValue().set(sessionKey, session);
        redisTemplate.expire(sessionKey, sessionTimeout, TimeUnit.SECONDS);
    }
}

在管理方法中获取redisTemplate对象是通过SpringUtils的公共方法
SpringUtils方法:

import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;

public class SpringUtils {

    private static WebApplicationContext webAppContext = ContextLoader.getCurrentWebApplicationContext();

    public static Object getBeanByName(String beanName) {
        return webAppContext.getBean(beanName);
    }

    public static Object getBeanByClass(Class<?> className) {
        return webAppContext.getBean(className);
    }
}

以上基本上搭建好了一个自定义的session。然后在模拟session的生产和使用情况。通过Filter或者Interceptor的方式:
SessionFilter方法:


import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import com.zcl.constants.Constants;
import com.zcl.session.bean.SystemSession;
import com.zcl.session.util.SessionManager;

public class SessionFilter implements Filter {

    private boolean openFlag = false;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String openFlagStr = filterConfig.getInitParameter("openFlag");
        openFlag = StringUtils.equals(openFlagStr, "true");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        // TODO Auto-generated method stub
        if(openFlag) {
            SystemSession session = null;
            //从请求从获取sessionId
            String sessionId = SessionManager.getSessionIdFromRequest((HttpServletRequest)request, Constants.SESSION_KEY);
            if(StringUtils.isNotBlank(sessionId)) {
                //判断此sessionId在redis中是否还存在
                session = SessionManager.getSessionFromRedis(sessionId);
            }

            //不存在,则创建新的
            if(session == null) {
                session = SessionManager.createSession(Constants.SESSION_KEY, sessionId,
                        (HttpServletRequest)request, (HttpServletResponse)response);
            }
            request.setAttribute(Constants.SESSION_KEY, session);
        }
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
    }

}

然后在web.xml中配置:

<filter>
    <filter-name>sessionFilter</filter-name>
    <filter-class>com.zcl.filter.SessionFilter</filter-class>
    <init-param>
        <param-name>openFlag</param-name>
        <param-value>true</param-value>
    </init-param>
 </filter>

<filter-mapping>
    <filter-name>sessionFilter</filter-name>
    <url-pattern>*.htm</url-pattern>
</filter-mapping>

此个filter应放在最先的位置。
在使用的地方,直接从httpServletRequest中获取即可

public SystemSession getSystemSession(HttpServletRequest request) {
        return (SystemSession)request.getAttribute(Constants.SESSION_KEY_USER);
    }

上门是使用的filter过滤器方式,下面修改成拦截器Interceptor方式,同时加上登录之后才能访问的地址过滤处理:
SecurityInterceptor

import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.zcl.constants.Constants;
import com.zcl.session.bean.SystemSession;
import com.zcl.session.util.SessionManager;
import com.zcl.util.CookieUtil;
import com.zcl.util.JSEscape;
import com.zcl.util.PropertyUtils;


/**
 * 判断用户权限,未登录用户跳转到登录页面
 * 
 * @ClassName: SecurityInterceptor
 * @Description: TODO
 * @date May 5, 2016 2:53:34 PM 
 * 
 *
 */
public class SecurityInterceptor extends HandlerInterceptorAdapter {

    // 需要安全验证的 URL
    private List<String> includedUrls;
    // 不需要安全过滤的 URL
    private List<String> excludedUrls;

    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub
        super.postHandle(request, response, handler, modelAndView);
    }
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        request.setAttribute("mid", PropertyUtils.getProperty("MLTrackerz_MID"));
        String requestUri = request.getRequestURI();

        //获取session信息
        SystemSession session = null;
        String sessionId = SessionManager.getSessionIdFromRequest(request, Constants.SESSION_KEY);
        if(sessionId != null){
            session = SessionManager.getSessionFromRedis(sessionId);
        }
        if (session == null) {
            session = SessionManager.createSession(Constants.SESSION_KEY, sessionId, request, response);
        }

        request.setAttribute(Constants.SESSION_KEY, session);

        /*
         * 需要登录才能访问url地址过滤
         */
        boolean mustLogin = false;
        if (includedUrls != null && includedUrls.size() > 0) {
            for (String url : includedUrls) {
                // 请求地址匹配到 includedUrls里面的任何一个地址,就要求登录
                if (requestUri.matches(url)) {
                    mustLogin = true;
                    break;
                }
            }
            // 请求地址没有匹配到 includedUrls里面的任何一个地址
            if (!mustLogin) {
                return true;
            }
        } else if (excludedUrls != null && excludedUrls.size() > 0) {
            for (String url : excludedUrls) {
                // 请求地址匹配到 excludedUrls里面地址,可以通过拦截器,不需要登录
                if (requestUri.matches(url)) {
                    return true;
                }
            }
        }

        //未登录时,跳到登录界面
        if (session == null
                || session.getAttribute(Constants.LOGIN_USER_KEY) == null) {
            // 记录登录前页面
            StringBuffer beforeLoginPage = request.getRequestURL();
            //参数
            if(StringUtils.isNotBlank(request.getQueryString())){
                beforeLoginPage.append("?").append(request.getQueryString());
            }
            // 只记录get请求
            if (request.getMethod().equalsIgnoreCase(
                    RequestMethod.GET.toString())
                    && beforeLoginPage.toString().indexOf("logout") == -1) {
                CookieUtil.addCookie(request, response, 
                        Constants.BEFORE_PAGE, JSEscape.escape(beforeLoginPage.toString()), null);
            }

            response.sendRedirect(request.getContextPath() + "/tologin");
            return false;

        }
        return super.preHandle(request, response, handler);
    }

    public void setExcludedUrls(List<String> excludedUrls) {
        this.excludedUrls = excludedUrls;
    }
    public void setIncludedUrls(List<String> includedUrls) {
        this.includedUrls = includedUrls;
    }
}

这时候context.xml中的配置要变成interceptor的方式了:

<bean id="securityInterceptor" class="com.zcl.interceptor.SecurityInterceptor">
        <property name="excludedUrls">
            <list>
                <value>/tologin.htm</value>
                <value>/loginout.htm</value>
            </list>
        </property>
        <property name="includedUrls">
            <list>
                <value>/myaddress.htm</value>
                <value>/mycredit.htm</value>
            </list>
        </property>
    </bean>

<mvc:interceptors>
    <!-- session/登录访问地址过滤处理 -->
    <mvc:interceptor>
        <mvc:mapping path="/*"/>
        <mvc:exclude-mapping path="/resource/**"/>
        <ref bean="securityInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

然后我们在controller中使用:

@RequestMapping(value={"/","index.htm"})
public ModelAndView index(HttpServletRequest request, ModelAndView mav) {
        SystemSession session = (SystemSession)request.getAttribute(Constants.SESSION_KEY);
        String userName = (String) session.getAttribute("userName");
        if(StringUtils.isBlank(userName)) {
            System.out.println("setUserName");
            session.setAttribute("userName", "zcl");
        }

        System.out.println("getUserName:" + userName);
        mav.setViewName("index");
        return mav;
    }

结果符合预期:
第一次请求时userName为NULL;第二次时为”zcl”。表示session唯一,同时放入session中的值也是跟随着用户的。

以上是我们单独构建的session处理,其实spring也提供了相关的封装,spring-session中已经封装了类似的处理。

springSession
观察上面我们的session处理过程,可以看出有两部分重点:
一是session怎样定义?session是个怎样的结果。
二是session怎样存放?因为在分布式环境下,session要采取啥方式存放;
三是session怎样与Request关联?因为每次请求中,都涉及到session的。
在上面的session构建中,通过redis方式存放session;通过cookie/header/参数方式与request/response关联。
知道了原理后,我们查看sping-session的源码,可以看出,实现的原理是一致的,只不过它提供了更好更多的扩展。
具体分析可以查看后面关于springSession学习的文章。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值