Session &Cookie

来源:互联网 发布:卖衣服打折怎么计算法 编辑:程序博客网 时间:2024/06/05 16:59

Session &Cookie

概念:

由于Http是无状态的,即一次http请求,服务端给出响应后,即断开服务,下次请求需要重建链接,在这两次的交互中,服务器是不知道这两次的客户端信息是否相同,为了跟踪用户的会话,提出了sessioncookie机制。

Cookie通过客户端来记录用户信息。

Session通过服务端来记录用户信息。

Cookie:实际上是一小段文本信息,每次客户端访问时都加上这段信息,这样服务器就能拿到request中的信息,进行解析,然后确认用户身份。

Session:客户端首次访问服务端时,服务端会生成用户的一些信息,用hashMap进行保存,同时会返回一个sessionId,以cookie的形式作为response返回给客户端,在之后的会话期间,客户端的请求加上sessionId即可,服务端拿到request之后,会根据sessionId,去hashMap中找有没有httpSession,有的话就拿出来使用。

具体介绍:

Cookie属性:

Namecookie的名称

Valuecookie的值,一般需要加密

maxAge:失效时间,负数表示临时cookie,关闭浏览器即失效;0表示删除该cookie,正数表示在maxAge秒之后失效。

Secure:是否使用安全传输协议。如httpsssl

Pathcookie的路径,必须要以“/”结束。如设置为“/root/,则只有contextPath"/root"的程序可以访问Cookie,如果设为"/",则代表本域下的contextPath都可访问该Cookie

Domain:域,必须以"."开头,如".jd.com",则所有"jd.com"结尾的域名服务都可以访问该cookie。如果跨域,比如360buy.com的旧接口就无法解析从.jd.com过来的请求。需要服务端进行特殊处理

Comment:备注

Version:版本

如:


Cookie生命周期:

主要是根据expire/max_age来判断,过期则失效。我们现在是用持久化cookie来解决会话问题,服务端保存了cookie用户的上下文对象,里面有cookie的创建时间,超时时间等,会对cookie的过期时间有个控制,拦截器来控制,比如用户请求后,服务端拿到cookie后,判断超时时间,如果剩余1/3的时间,可以重新刷新超时时间。

    public String intercept(ActionInvocation invocation) throws Exception {

        //如果登录成功,则继续调用,否则返回

        //判断是否配置了cookiecookie名称

        ActionContext actionContext = invocation.getInvocationContext();

        HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);

        HttpServletResponse response = (HttpServletResponse) actionContext.get(StrutsStatics.HTTP_RESPONSE);

        String value = cookieUtils.getCookieValue(request, loginCookieKey);

        if (StringUtils.isNotBlank(value)) {//能取到值

            LoginContext context = getLoginContext(value);

            if (context != null) {//又能解出来

                long current = System.currentTimeMillis();

                long created = context.getCreated();

                long expires = context.getExpires();

                long timeout = expires == 0 ? sessionTimeout * 1000 : expires - created;//如果没有设置过期时间,则使用默认的

                if (current - created < timeout) { //如果没有过期

                    LoginContext.setLoginContext(context);

                    if ((current - created) * rate > timeout) {//如果剩下的时间只有2/3,就需要重新派发cookie

                        log.error("session cookie[" + loginCookieKey + "] rewrite!");

                        //写最后一次访问的cookie

                        context.setCreated(current);

                        if (expires != 0) {

                            context.setTimeout(timeout);

                        }

                        cookieUtils.setCookie(response, loginCookieKey, context.toCookieValue());

                    }

                    //如果没过期,则继续调用

                    return invocation.invoke();

                } else {

                    log.error("session cookie[" + loginCookieKey + "] is valid!");

                    //超时后,要清空 ,返回客户端

                    cookieUtils.invalidate(request, response);

                }

            } else {

                log.error("session cookie[" + loginCookieKey + "] is error!");

            }

        }

Session生命周期:

为提高效率,服务器一般把session放入内存。每个用户都会有独立的session。如果session过于复杂,大量用户访问时就会内存溢出,所以,session应越小越好。Session是用户第一次访问服务器的时候创建的。用户关闭浏览器后session事实上并没有消失,还在内存中,一直等到过期才会消失。平时所说的消失,其实是指用户关闭浏览器重新打开后重新生成了新的session而已。Session生成后,只要用户继续访问,服务器就会更新session的最后访问时间,并维护该session

Session属性:

setAttribute:map设值

getAttribute:获取value

getId():获取sessionid

Session共享:

1. 单独的session应用服务器

2. Session保存到缓存服务器中。

3. 基于NFSNet File System),只需将共享目录服务器mount到各频道服务器的本地session目录即可。缺点是NFS依托于复杂的安全机制和文件系统,因此并发效率不高,尤其对于session这类高并发读写的小文件,会由于共享目录服务器io过高,拖垮web应用程序执行效率。

4. 基于数据库session共享。

5. 基于cookiesession共享: 将用户的session信息机密,序列化,统一放在根域名下。利用浏览器访问该根域名下的所有二级域名站点时,会传递与之域名对应的所有cookie内容的特性,从而事项用户的cookiesession在多服务间的共享。该方案不需要额外的服务器资源,但受到http协议头长度的限制,所以cookie在传递的时候要小,而且还要有有效的加密措施。这种策略优势比较明显,目前公司采用的就是这种方式。

Cookie模拟session的实例

1. 客户端获取RSA公钥

 public String execute() {

        try {

            log.info("-----------存入公钥开始----------");

            KeyPair keyPair = RSABuilder.generateKeyPair();

            PrivateKey privateKey = keyPair.getPrivate();

            this.privateKeyStr = Base64.encodeBytes(privateKey.getEncoded());

            PublicKey publicKey = keyPair.getPublic();

            this.publicKeyStr = Base64.encodeBytes(RSABuilder.confuse(publicKey.getEncoded()));

            returnResult.setCode(ResultCode.SUCCESS.getCode());

            returnResult.setMessage(this.publicKeyStr);//把公钥返回给客户端

            StringBuilder key = new StringBuilder();

            key.append(c.getUuid());

            if(StringUtils.isNotBlank(c.getAsid())){

                key.append(c.getAsid());

            }

            cacheUtils.set(key.toString(), 60 * 5, this.privateKeyStr);

            log.info("uuid" + c.getUuid() + "|publicKey:" + publicKeyStr + "|privateKey:" + privateKeyStr);

        } catch (Exception e) {

            log.error(e);

        }

        return SUCCESS;

    }

2. 客户端获取DES私钥

     RsaDecoder rsaDecoder = RsaDecoder.getInstance(privateKey);

            String desKey = rsaDecoder.dencrypt(envelopeKey);//解密客户端传过来的随机数

            log.info("uuid:" + c.getUuid() + "|dencryptContent:" + desKey);

            HttpServletResponse response = (HttpServletResponse) ActionContext.getContext().get(StrutsStatics.HTTP_RESPONSE);

            JdAppLoginContext context = JdAppLoginContext.getLoginContext();

            if (context == null) {

                context = new JdAppLoginContext();

            }

            String sessionKey = DESCoder.initKey();

            log.info("uuid:" + c.getUuid() + "sessionKey:" + sessionKey);

            context.setSessionKey(sessionKey);

            JdAppLoginContext.setLoginContext(context);

            loginCookieUtil.setTwoDomainCookie(request, response, loginCookieKey, context.toCookieValue());

            returnResult.setCode(ResultCode.SUCCESS.getCode());

            //服务端生成一个sessionkey,用客户端传过来的key把这个sessionkey加密,然后发给客户端,之后客户端用这个sessionkey进行正文加密,服务端再解开就行了

            String message = DesUtil.encrypt(sessionKey, desKey);

            returnResult.setMessage(message);

3. 加密登录信息进行登录,成功后写cookie

       if (loginAndRegisterResult.isSuccess()) {

                HttpServletResponse response = (HttpServletResponse) ActionContext.getContext().get(StrutsStatics.HTTP_RESPONSE);

                JdAppLoginContext context = JdAppLoginContext.getLoginContext();

                if (context == null) {

                    context = new JdAppLoginContext();

                }

                context.setPin(loginAndRegisterResult.getUserPin()); //设置pin

                JdAppLoginContext.setLoginContext(context);

                String cookieValue = context.toCookieValue();

                //用新的包装方法设置cookie,保证如果是360buy请求的就设为360buydomain,如果是jd请求的,就设为jddomain

                loginCookieUtil.setTwoDomainCookie(request,response, loginCookieKey, cookieValue);

                //cookie的同时写入响应体中

                loginAndRegisterResult.setAppLogin(cookieValue);

                //设置响应体,让wap网关不代理

                response.setContentType("application/json;charset=utf-8;X-Wap-Proxy-Cookie=none");

                //写入日志:

                loginAnalysisService.insert(request, "音乐登录", loginAndRegisterResult.getUserPin());

            }

4. 用户下次访问时,先进拦截器,拿到客户端cookie,解密,看能否解开,如果能解开说明已经登录,如果解不开说明没登陆。

     String value = loginCookieUtil.getCookieValue(request, loginCookieKey);       

        log.error("**************************** HttpServletRequest中拿cookie-----"+"loginCookieKey等于"+loginCookieKey+"  结果"+value);

        if (StringUtils.isNotBlank(value)) {//能取到值

            LoginContext context = getLoginContext(value);

                long current = System.currentTimeMillis();

                long created = context.getCreated();

                long expires = context.getExpires();

                long timeout = expires == 0 ? sessionTimeout * 1000 : expires - created;//如果没有设置过期时间,则使用默认的

                if (current - created < timeout) { //如果没有过期

                    LoginContext.setLoginContext(context);

                    if ((current - created) * rate > timeout) {//如果剩下的时间只有2/3,就需要重新派发cookie

                        log.error("session cookie[" + loginCookieKey + "] rewrite!");

                        //写最后一次访问的cookie

                        context.setCreated(current);

                        if (expires != 0) {

                            context.setTimeout(timeout);

                        }

                        loginCookieUtil.setCookie(response, loginCookieKey, context.toCookieValue());

                    }

                    //如果没过期,则继续调用

                    return invocation.invoke();

                } else {

                    log.error("session cookie[" + loginCookieKey + "] is valid!");

                    //超时后,要清空 ,返回客户端

                    loginCookieUtil.invalidate(request, response);

                }

        }

跟踪客户状态的方法:

1. 建立含有跟踪数据的隐藏字段,即hidden

2. 重写包含额外参数的URL

3. 使用持续的Cookie

4. 使用Session

区别:

1. Cookie是保存在客户端的,session是保存在服务端的。

2. Cookie不是很安全,可以分析本地的cookie进行cookie欺骗。

3. Session保存在服务器上,如果过期时间较长,会占用服务器资源。考虑减轻服务器性能,可以考虑Cookie

4. 用Session的话,需要考虑session共享问题,如分布式缓存,单独的session服务器等。


0 0
原创粉丝点击