cookie和session

  对于cookie和session,作为程序员的我们都听说过或者接触过,比如购物车就会用到,算是老生常谈了。不过不细谈还真不好把他们俩搞清楚。标题把cookie放在前面,是因为它出生的早,没有cookie,就没有session。但是有了session后,大家都不喜欢用cookie了。为啥呢?接下来会聊到。

  首先我们需要搞清楚什么是cookie,什么是session,他们有什么共性和特性。cookie和session都是客户端和服务端之间的沟通,而且都是服务端为了克服http协议的无状态特性而利用了客户端。当服务端需要区分不同的客户端或者想往客户端里寄存点东西时,在响应的消息头Set-Cookie里设置相关信息(注意这里是响应消息头),通过一个字符串来存放一些键值对。客户端收到后根据域名、路径地址等信息分门别类存起来,这个存起来的本地文件就是cookie。下一次客户端再次请求服务端时,会把cookie通过消息头Cookie(注意这里是请求消息头)带过去,服务端取到cookie就能识别客户端了,也能取到之前寄存在客户端的货物了。

  session的原理跟cookie是一样的,也是寄存货物在客户端,唯一不同的是寄存的是一个叫JSESSIONID的字符串,而真正的货物实际上还是在服务端的。拿登陆举例,登陆校验通过后通过响应的Set-Cookie消息头把JSESSIONID和一个服务端随机生成的字符串带给客户端寄存(当然服务端自己也有存底的)。我们可以把这个随机串看做寄存的凭条,客户端是小储物柜,只存了这张小纸片。而服务端是大储物柜,存放着真正有价值的大货物——客户信息。当客户端再次请求时通过Cookie消息头把这张凭条带给服务端,服务端照着凭条到自己的客户明细表里一查,就能找到对应的包裹——客户信息对象,这时就能拿到用户名和密码了。  

  说完联系,再说它们的不同点。对服务端而言,它关心的不是寄存的凭条,而是真正的包裹——客户信息,也就是session的值。这些真实货物存放的地方不在客户端,而是服务端。即使cookie失效了,session不一定也失效。cookie的失效时间分临时性和持久性两种,默认是临时性的,也就是随着客户端的打开而生、关闭而死的。想要延长寿命,就得启用持久性方式,写入本地文件中。临时性方式中,JSESSIONID作为一个cookie,存在于浏览器的内存里,每次新打开浏览器窗口,就是一个cookie新生命的诞生,都会实例化一个新session出来。自然的,服务端寄存的老货也就没用了。所以需要注意,用不到的session要设置失效,好让服务端清理这些无主之物,不然可能会把服务端内存撑爆的。

  回来开篇的问题,有了session,大家都不用cookie了,这是为啥?两个方面,一个是cookie在每次请求时都会传送,这会加大网络传输的压力;另一个是cookie是放在消息头的,而且本地cookie文件的大小不能超过4KB,这就限制了cookie能存放的信息量。而这两个问题session都不存在,因为人家是存在服务端的,消息头中只要放一个JSESSIONID字符串就够了。话说回来,session还是离不开cookie的帮忙,那么最致命的问题来了,cookie被禁用了怎么办?还好有后门——URL重写,把JSESSIONID作为参数拼接到url里面去传给客端户,再由客户端传回服务端。

  说了半天,看个例子吧,还是拿登陆说事:

  登陆的action:

/**
     * 执行登陆行为
     *
     * @author wulinfeng
     * @param request
     * @param user
     * @return
     * @throws ServletException
     * @throws IOException
     */
    @RequestMapping(value = "/loginAction.html", method = RequestMethod.POST)
    public @ResponseBody void loginAction(HttpServletRequest request, HttpServletResponse response,
        @RequestBody UserBean user)
        throws ServletException, IOException
    {
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter out = response.getWriter();
        
        // 验证码校验
        String validateCode = (String)request.getSession().getAttribute("randomString");
        if (StringUtils.isEmpty(validateCode) || !validateCode.equals(user.getValidate().toUpperCase()))
        {
            out.print(PropertiesConfigUtil.getProperty("verify_code_error"));
            out.flush();
            return;
        }
        
        // 用户名密码校验
        String result = testPillingService.login(user.getUsername(), user.getPassword());
        
        // 校验通过,创建token并放入session中;校验失败,返回错误描述
        if ("success".equals(result))
        {
            String tokenId = UUID.randomUUID().toString();
            
            // 登陆成功后是使用cookie还是session来存放tokenId
            if (IS_COOKIE.equals("1"))
            {
                Cookie cookie = new Cookie("tokenId", tokenId);
                cookie.setMaxAge(3 * 24 * 60 * 60); // 3天过期
                response.addCookie(cookie);
            }
            else
            {
                request.getSession(true).setAttribute("tokenId", tokenId);
            }
            
            if (user.getUsername().toUpperCase().equals("ADMIN"))
            {
                response.getWriter().print("register");
                return;
            }
        }
        out.print(result);
        out.flush();
    }

  拦截器:

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * 登陆拦截
 *
 * @author wulinfeng
 * @version C10 2017年10月11日
 * @since SDP V300R003C10
 */
public class InterceptorUtil implements HandlerInterceptor
{
    /** 日志对象 */
    private static Logger logger = LogManager.getLogger(InterceptorUtil.class.getName());
    
    /** 是否启用cookie */
    private static final String IS_COOKIE = PropertiesConfigUtil.getProperty("iscookie");
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception
    {
        logger.debug("InterceptorUtil.doFilter requesturl: " + request.getRequestURL());
        
        String tokenId = null;
        
        if (IS_COOKIE.equals("1"))
        {
            Cookie[] cookies = request.getCookies();
            if (cookies != null)
            {
                for (Cookie cookie : cookies)
                {
                    if (cookie == null)
                    {
                        continue;
                    }
                    if (cookie.getName().equals("tokenId"))
                    {
                        tokenId = cookie.getValue();
                        break;
                    }
                }
            }
        }
        else
        {
            if (request.getSession() != null)
            {
                tokenId = (String)request.getSession().getAttribute("tokenId");
            }
        }
        
        if (StringUtils.isEmpty(tokenId))
        {
            response.sendRedirect("/login.html");
        }
        
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
        ModelAndView modelAndView)
        throws Exception
    {
        // TODO Auto-generated method stub
        
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
        throws Exception
    {
        // TODO Auto-generated method stub
        
    }
    
}

   这里登陆校验通过时我们通过cookie或session把tokenId放入回话,再次请求时通过拦截器判断tokenId是否已经在会话里,是则说明该用户已登陆过,否则是未登录用户,给出页面提示,并跳转到登陆页面。

原文地址:https://www.cnblogs.com/wuxun1997/p/7725413.html