session_sessionstorage - CSDN
session 订阅
Session:在计算机中,尤其是在网络应用中,称为“会话控制”。Session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的Web页之间跳转时,存储在Session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web页时,如果该用户还没有会话,则Web服务器将自动创建一个 Session对象。当会话过期或被放弃后,服务器将终止该会话。Session 对象最常见的一个用法就是存储用户的首选项。例如,如果用户指明不喜欢查看图形,就可以将该信息存储在Session对象中。有关使用Session 对象的详细信息,请参阅“ASP应用程序”部分的“管理会话”。注意会话状态仅在支持cookie的浏览器中保留。 展开全文
Session:在计算机中,尤其是在网络应用中,称为“会话控制”。Session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的Web页之间跳转时,存储在Session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web页时,如果该用户还没有会话,则Web服务器将自动创建一个 Session对象。当会话过期或被放弃后,服务器将终止该会话。Session 对象最常见的一个用法就是存储用户的首选项。例如,如果用户指明不喜欢查看图形,就可以将该信息存储在Session对象中。有关使用Session 对象的详细信息,请参阅“ASP应用程序”部分的“管理会话”。注意会话状态仅在支持cookie的浏览器中保留。
信息
外文名
Session
应用学科
软件 网络通信
中文名
时域
属    性
用户与交互系统通信的时间间隔
sessionICE中间件自定义Session机制的远程监控系统
为了满足实时远程监测系统的需求,服务器需要实时监测客户端的连接状态。为此利用ICE中间件的优点设计了一种基于面向对象ICE中间件自定义Session机制的解决方案。 [1]  Session直接翻译成中文比较困难,一般都译成时域。在计算机专业术语中,Session是指一个终端用户与交互系统进行通信的时间间隔,通常指从注册进入系统到注销退出系统之间所经过的时间。以及如果需要的话,可能还有一定的操作空间。需要注意的是,一个Session的概念需要包括特定的客户端,特定的服务器端以及不中断的操作时间。A用户和C服务器建立连接时所处的Session同B用户和C服务器建立连接时所处的Session是两个不同的Session。session的工作原理:(1)当一个session第一次被启用时,一个独一的标识被存储于本地的cookie中。(2)首先使用session_start()函数,PHP从session仓库中加载已经存储的session变量。(3)当执行PHP脚本时,通过使用session_register()函数注册session变量。(4)当PHP脚本执行结束时,未被销毁的session变量会被自动保存在本地一定路径下的session库中,这个路径可以通过php.ini文件中的session.save_path指定,下次浏览网页时可以加载使用。那什么是Session的解决方案呢?用户访问一个网站时往往需要浏览许多网页。Session的使用在不同的语言中的使用方法特点不尽相同。对于一个通过PHP构筑的网站来说,用户在访问的过程中需要执行许多的PHP脚本。然而由于HTTP协议自身的特点,用户每执行一个PHP脚本都需要和Web服务器重新建立连接。又由于无状态记忆的特点,此次连接无法得到上次连接的状态。这样,用户在一个PHP脚本中对一个变量进行了赋值操作,而在另外一个PHP脚本中却无法得到这个变量的值。例如,用户在负责登录的PHP脚本中设置了$user="wind",却无法在另一个PHP脚本中通过调用$user来获得“wind”这个值。也就是说,在一次HTTP请求中,PHP无法将已经赋值的参数传递给下一次http请求的脚本。因此,每个PHP脚本中所定义的变量都是只在本次HTTP请求内有效,本次HTTP请求结束,PHP就会释放掉这些为这些变量分配的内存。Session解决方案,就是要提供在PHP脚本中定义全局变量的方法,使得这个全局变量在同一个Session中对于所有的PHP脚本都有效。提到了,Session不是一个简单的时间概念,一个Session中还包括了特定的用户和服务器。因此更详细地讲,在一个Session定义的全局变量的作用范围,是指这个Session所对应的用户所访问的所有PHP。例如A用户通过Session定义了一个全局变量$user=“wind”中,而B用户通过Session定义的全局变量$user=“jane”。那么在A用户所访问的PHP脚本中,$user的值就是wind。 [1]  Session 是 用于保持状态的基于Web服务器的方法。Session允许通过将对象存储在Web服务器的内存中在整个用户会话过程中保持任何对象。Session通常用于执行以下操作存储需要在整个用户会话过程中保持其状态的信息,例如登录信息或用户浏览Web应用程序时需要的其它信息。存储只需要在页面重新加载过程中或按功能分组的一组页之间保持其状态的对象。Session的作用就是它在Web服务器上保持用户的状态信息供在任何时间从任何设备上的页面进行访问。因为浏览器不需要存储任何这种信息,所以可以使用任何浏览器,即使是像Pad或手机这样的浏览器设备。持久性方法的限制随着越来越多用户登录,Session所需要的服务器内存量也会不断增加。访问Web应用程序的每个用户都生成一个单独的Session对象。每个Session对象的持续时间是用户访问的时间加上不活动的时间。如果每个Session中保持许多对象,并且许多用户同时使用Web应用程序(创建许多Session),则用于 Session持久性的服务器内存量可能会很大,从而影响了可伸缩性。 [1] 
收起全文
精华内容
参与话题
  • Session

    千次阅读 多人点赞 2019-02-08 22:43:26
    Session是服务器为每个访问这个服务器的客户端用户创建的一个容器。这个容器中存储的数据能够在多个request之间实现共享。而且,这个容器只属于当前这个用户。 Session是怎么标识每一个用户的 表面上,我们说Session...

    Session是服务器为每个访问这个服务器的客户端用户创建的一个容器。这个容器中存储的数据能够在多个request之间实现共享。而且,这个容器只属于当前这个用户。
    Session是怎么标识每一个用户的
    表面上,我们说Session容器是属于某一个用户。其实在会话过程中,只有客户端(浏览器)和服务器两个角色。所以,Session容器标识的不是用户,而是当前用户正在使用的浏览器。浏览器与服务器交流是通过给服务器发送请求实现的,浏览器的每次请求中如果都有一个相同的标记用来标记session。服务器就能够识别这个用户了。

    这个标记叫做JSESSIONID。我们重新演示,cookie的setPath练习,发现服务器启动后,浏览器的每次请求都会携带一个name属性值为"JSESSIONID"的cookie,这个cookie的值在每次请求过程中都是相同的。

    Session的作用
    为每个访问服务器的用户创建一个存储数据的容器;
    容器中的数据在多个请求之间共享;

    获得Session对象API
    Session容器的创建,调用的方法是:request.getSession()。

    //获取Session容器对象(创建session对象)
       HttpSession session = request.getSession();
       System.out.println(session);
    

    session常用API
    在这里插入图片描述实践:
    需求:

    1. 在第一个Servlet中往Servlet中存储数据;
    2. 在另外两个Servlet中获取session和session中存储的值,分别打印session和值;
    //往session中设置值
    @WebServlet("/sessionone")
    public class SessionOneServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //在session中存储值
            //获取session
            HttpSession session = request.getSession();
            //储存值
            session.setAttribute("session","sessionvlaue");
        }
    }
    
    //从session中取值
    @WebServlet("/sessontwo")
    public class SessionTwoServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //获取session中存储的值
            //获取session
            HttpSession session = request.getSession();
            String session1 = (String) session.getAttribute("session");
            System.out.println("session1 = " + session1);
        }
    }
    

    Session的生命周期
    当客户端浏览器第一次访问服务器时,服务器默认为每个浏览器创建不同的HttpSession对象。我们可以使用request.getSession()方法来获得HttpSession对象。

    当第一次执行 request.getSession()时,有session对象就返回创建的session,没有session就创建session对象。后续的request.getSession()只能获取已创建的session。

    Session销毁
    方式一:时间超出了session的存活时间
    session的默认存活时间是30分钟,在tomcat的全局配置文件web.xml中。(路径:tomcat/config/web.xml)
    我们可以在web.xml中自己设置这个存活时间。我们设置的这个时间会覆盖原来的存活时间。

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    		  http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd"
               version="3.1">
        <!--将session的存活时间设置成1-->
        <session-config>
            <session-timeout>1</session-timeout>
        </session-config>
    </web-app>
    
    //获取session
            HttpSession session = request.getSession();
            //打印session和sessionId
            System.out.println("session="+session+";sessionId="+session.getId());
    

    注意: Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,并维护该Session。用户每访问服务器一次,无论是否读写Session,服务器都认为该用户的Session"活跃(active)"了一次。

    方式二:在Servlet中手动销毁
    手动销毁session,调用的方法是:session.invalidate()方法;

     //可以删除session
            HttpSession session = request.getSession();//可以获取session
            System.out.println(session.getId());
    
            session.invalidate();//session被销毁了
    

    浏览器关闭后,session持久化方案
    通过上面的例子我们发现,浏览器关闭后,JSESSIONID就消失了,再次访问的时候又重新创建了一个新的session对象。这样,是比较消耗资源的。
    session之所以重新创建是因为,浏览器关闭后JESSIONID这个cookie消失了。所以,就不能够在标识这个session了。如果能够让cookie不消失(或者存活时间长点)就能够在很长一段时间内把这个标识发送给Servlet了。此时Servlet就能够找到之前创建的session对象了。
    【实现方案】
    在servlet中手动创建JESSIONID;
    手动设置JESSIONID的存活时间;
    将JESSIONID响应给浏览器;

    @WebServlet("/chi")
    public class ChiServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //实现持久化session
            HttpSession session = request.getSession();
            //获取sessionid
            String id = session.getId();
    
            Cookie cookie = new Cookie("JSESSIONID",id);
            //是cookie持久化
            cookie.setMaxAge(60*30);
    
            //响应回浏览器
            response.addCookie(cookie);
        }
    }
    

    Servlet作用域总结

    • ServletContext域:
      一个WEB应用(项目)对应一个ServletContext,这个对象中保存的数据正在整个WEB项目中都有效;
      • 创建:服务器启动的时候;
      • 销毁:服务器关闭或项目移除后;
    • HttpSession:
      一次会话给客户端(浏览器)创建一个session。这个对象中保存的数据,一次会话(多次请求)内数据有效;
      • 创建:服务器第一次调用getSession()的时候;
      • 销毁:
        • 服务器非正常关闭(正常关闭:Session被序列化);
        • Session过期了:默认存活时间30分钟;
        • 手动调用session的invalidate()方法;
    • HttpServletRequest:
      一次请求创建一个request。这个对象中保存的数据,一次请求(请求链)内数据有效;
      • 创建:客户端向服务器发送一次请求;
      • 销毁:服务器为这次请求做出响应之后,销毁request;

    【API操作】操作三个作用域对象的API

    • 存储数据:setAttribute(name,value);
    • 获得数据:getAttribute(name);
    • 删除数据:removeAttribute(name);
    展开全文
  • Cookie/Session机制详解

    万次阅读 多人点赞 2011-11-10 09:25:12
    会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。 本章将系统地...


    会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份Session通过在服务器端记录信息确定用户身份

    本章将系统地讲述Cookie与Session机制,并比较说明什么时候不能用Cookie,什么时候不能用Session。


    1.1  Cookie机制

    在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么时间购买的,这都是属于同一个会话的,不能放入用户B或用户C的购物车内,这不属于同一个会话。

    而Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话即用户A购买了一件商品放入购物车内,当再次购买商品时服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话,必须引入一种机制。

    Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用Cookie来跟踪会话。

    1.1.1  什么是Cookie

    Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。

    由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理

    Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。



    查看某个网站颁发的Cookie很简单。在浏览器地址栏输入javascript:alert (document. cookie)就可以了(需要有网才能查看)。JavaScript脚本会弹出一个对话框显示本网站颁发的所有Cookie的内容,如图1.1所示。


    图1.1  Baidu网站颁发的Cookie


    图1.1中弹出的对话框中显示的为Baidu网站的Cookie。其中第一行BAIDUID记录的就是笔者的身份helloweenvsfei,只是Baidu使用特殊的方法将Cookie信息加密了。


    注意:Cookie功能需要浏览器的支持。

    如果浏览器不支持Cookie(如大部分手机中的浏览器)或者把Cookie禁用了,Cookie功能就会失效。

    不同的浏览器采用不同的方式保存Cookie。

    IE浏览器会在“C:\Documents and Settings\你的用户名\Cookies”文件夹下以文本文件形式保存,一个文本文件保存一个Cookie。


    1.1.2  记录用户访问次数

    Java中把Cookie封装成了javax.servlet.http.Cookie类。每个Cookie都是该Cookie类的对象。服务器通过操作Cookie类对象对客户端Cookie进行操作。通过request.getCookie()获取客户端提交的所有Cookie(以Cookie[]数组形式返回),通过response.addCookie(Cookiecookie)向客户端设置Cookie。

    Cookie对象使用key-value属性对的形式保存用户状态,一个Cookie对象保存一个属性对,一个request或者response同时使用多个Cookie。因为Cookie类位于包javax.servlet.http.*下面,所以JSP中不需要import该类。


    1.1.3  Cookie的不可跨域名性

    很多网站都会使用Cookie。例如,Google会向客户端颁发Cookie,Baidu也会向客户端颁发Cookie。那浏览器访问Google会不会也携带上Baidu颁发的Cookie呢?或者Google能不能修改Baidu颁发的Cookie呢?

    答案是否定的。Cookie具有不可跨域名性根据Cookie规范,浏览器访问Google只会携带Google的Cookie,而不会携带Baidu的Cookie。Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。

    Cookie在客户端是由浏览器来管理的。浏览器能够保证Google只会操作Google的Cookie而不会操作Baidu的Cookie,从而保证用户的隐私安全。浏览器判断一个网站是否能操作另一个网站Cookie的依据是域名。Google与Baidu的域名不一样,因此Google不能操作Baidu的Cookie。

    需要注意的是,虽然网站images.google.com与网站www.google.com同属于Google,但是域名不一样,二者同样不能互相操作彼此的Cookie。


    注意:用户登录网站www.google.com之后会发现访问images.google.com时登录信息仍然有效,而普通的Cookie是做不到的。这是因为Google做了特殊处理。本章后面也会对Cookie做类似的处理。


    1.1.4  Unicode编码:保存中文

    中文与英文字符不同,中文属于Unicode字符,在内存中占4个字符,而英文属于ASCII字符,内存中只占2个字节。Cookie中使用Unicode字符时需要对Unicode字符进行编码,否则会乱码。


    提示:Cookie中保存中文只能编码。一般使用UTF-8编码即可。不推荐使用GBK等中文编码,因为浏览器不一定支持,而且JavaScript也不支持GBK编码。


    1.1.5  BASE64编码:保存二进制图片

    Cookie不仅可以使用ASCII字符与Unicode字符,还可以使用二进制数据。例如在Cookie中使用数字证书,提供安全度。使用二进制数据时也需要进行编码。

    %注意:本程序仅用于展示Cookie中可以存储二进制内容,并不实用。由于浏览器每次请求服务器都会携带Cookie,因此Cookie内容不宜过多,否则影响速度。Cookie的内容应该少而精。


    1.1.6  设置Cookie的所有属性

    除了name与value之外,Cookie还具有其他几个常用的属性。每个属性对应一个getter方法与一个setter方法。Cookie类的所有属性如表1.1所示。

    表1.1  Cookie常用属性

    属  性  名

    描    述

    String name

    该Cookie的名称。Cookie一旦创建,名称便不可更改

    Object value

    该Cookie的值。如果值为Unicode字符,需要为字符编码。如果值为二进制数据,则需要使用BASE64编码

    int maxAge

    该Cookie失效的时间,单位秒。如果为正数,则该Cookie在maxAge秒之后失效。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为–1

    boolean secure

    该Cookie是否仅被使用安全协议传输。安全协议。安全协议有HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false

    String path

    该Cookie的使用路径。如果设置为“/sessionWeb/”,则只有contextPath为“/sessionWeb”的程序可以访问该Cookie。如果设置为“/”,则本域名下contextPath都可以访问该Cookie。注意最后一个字符必须为“/”

    String domain

    可以访问该Cookie的域名。如果设置为“.google.com”,则所有以“google.com”结尾的域名都可以访问该Cookie。注意第一个字符必须为“.”

    String comment

    该Cookie的用处说明。浏览器显示Cookie信息的时候显示该说明

    int version

    该Cookie使用的版本号。0表示遵循Netscape的Cookie规范,1表示遵循W3C的RFC 2109规范


    1.1.7  Cookie的有效期

    Cookie的maxAge决定着Cookie的有效期,单位为秒(Second)。Cookie中通过getMaxAge()方法与setMaxAge(int maxAge)方法来读写maxAge属性。

    如果maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到对应的Cookie文件中。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。下面代码中的Cookie信息将永远有效。


    Cookie cookie = new Cookie("username","helloweenvsfei");   // 新建Cookie

    cookie.setMaxAge(Integer.MAX_VALUE);           // 设置生命周期为MAX_VALUE

    response.addCookie(cookie);                    // 输出到客户端


    如果maxAge为负数,则表示该Cookie仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该Cookie即失效。maxAge为负数的Cookie,为临时性Cookie,不会被持久化,不会被写到Cookie文件中。Cookie信息保存在浏览器内存中,因此关闭浏览器该Cookie就消失了。Cookie默认的maxAge值为–1。

    如果maxAge为0,则表示删除该Cookie。Cookie机制没有提供删除Cookie的方法,因此通过设置该Cookie即时失效实现删除Cookie的效果。失效的Cookie会被浏览器从Cookie文件或者内存中删除,


    例如:

    Cookie cookie = new Cookie("username","helloweenvsfei");   // 新建Cookie

    cookie.setMaxAge(0);                          // 设置生命周期为0,不能为负数

    response.addCookie(cookie);                    // 必须执行这一句


    response对象提供的Cookie操作方法只有一个添加操作add(Cookie cookie)。

    要想修改Cookie只能使用一个同名的Cookie来覆盖原来的Cookie,达到修改的目的。删除时只需要把maxAge修改为0即可。


    注意:从客户端读取Cookie时,包括maxAge在内的其他属性都是不可读的,也不会被提交。浏览器提交Cookie时只会提交name与value属性。maxAge属性只被浏览器用来判断Cookie是否过期。


    1.1.8  Cookie的修改、删除

    Cookie并不提供修改、删除操作。如果要修改某个Cookie,只需要新建一个同名的Cookie,添加到response中覆盖原来的Cookie。

    如果要删除某个Cookie,只需要新建一个同名的Cookie,并将maxAge设置为0,并添加到response中覆盖原来的Cookie。注意是0而不是负数。负数代表其他的意义。读者可以通过上例的程序进行验证,设置不同的属性。


    注意:修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。


    1.1.9  Cookie的域名

    Cookie是不可跨域名的。域名www.google.com颁发的Cookie不会被提交到域名www.baidu.com去。这是由Cookie的隐私安全机制决定的。隐私安全机制能够禁止网站非法获取其他网站的Cookie。

    正常情况下,同一个一级域名下的两个二级域名如www.helloweenvsfei.com和images.helloweenvsfei.com也不能交互使用Cookie,因为二者的域名并不严格相同。如果想所有helloweenvsfei.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数,例如:

    Cookie cookie = new Cookie("time","20080808"); // 新建Cookie

    cookie.setDomain(".helloweenvsfei.com");           // 设置域名

    cookie.setPath("/");                              // 设置路径

    cookie.setMaxAge(Integer.MAX_VALUE);               // 设置有效期

    response.addCookie(cookie);                       // 输出到客户端


    读者可以修改本机C:\WINDOWS\system32\drivers\etc下的hosts文件来配置多个临时域名,然后使用setCookie.jsp程序来设置跨域名Cookie验证domain属性。

    注意:domain参数必须以点(".")开始。另外,name相同但domain不同的两个Cookie是两个不同的Cookie。如果想要两个域名完全不同的网站共有Cookie,可以生成两个Cookie,domain属性分别为两个域名,输出到客户端。


    1.1.10  Cookie的路径

    domain属性决定运行访问Cookie的域名,而path属性决定允许访问Cookie的路径(ContextPath)。例如,如果只允许/sessionWeb/下的程序使用Cookie,可以这么写:

    Cookie cookie = new Cookie("time","20080808");     // 新建Cookie

    cookie.setPath("/session/");                          // 设置路径

    response.addCookie(cookie);                           // 输出到客户端

    设置为“/”时允许所有路径使用Cookie。path属性需要使用符号“/”结尾。name相同但domain相同的两个Cookie也是两个不同的Cookie。


    注意:页面只能获取它属于的Path的Cookie。例如/session/test/a.jsp不能获取到路径为/session/abc/的Cookie。使用时一定要注意。


    1.1.11  Cookie的安全属性

    HTTP协议不仅是无状态的,而且是不安全的。使用HTTP协议的数据不经过任何加密就直接在网络上传播,有被截获的可能。使用HTTP协议传输很机密的内容是一种隐患。如果不希望Cookie在HTTP等非安全协议中传输,可以设置Cookie的secure属性为true。浏览器只会在HTTPS和SSL等安全协议中传输此类Cookie。下面的代码设置secure属性为true:


    Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie

    cookie.setSecure(true);                           // 设置安全属性

    response.addCookie(cookie);                        // 输出到客户端


    提示:secure属性并不能对Cookie内容加密,因而不能保证绝对的安全性。如果需要高安全性,需要在程序中对Cookie内容加密、解密,以防泄密。


    1.1.12  JavaScript操作Cookie

    Cookie是保存在浏览器端的,因此浏览器具有操作Cookie的先决条件。浏览器可以使用脚本程序如JavaScript或者VBScript等操作Cookie。这里以JavaScript为例介绍常用的Cookie操作。例如下面的代码会输出本页面所有的Cookie。

    <script>document.write(document.cookie);</script>

    由于JavaScript能够任意地读写Cookie,有些好事者便想使用JavaScript程序去窥探用户在其他网站的Cookie。不过这是徒劳的,W3C组织早就意识到JavaScript对Cookie的读写所带来的安全隐患并加以防备了,W3C标准的浏览器会阻止JavaScript读写任何不属于自己网站的Cookie。换句话说,A网站的JavaScript程序读写B网站的Cookie不会有任何结果。


    1.1.13  案例:永久登录

    如果用户是在自己家的电脑上上网,登录时就可以记住他的登录信息,下次访问时不需要再次登录,直接访问即可。实现方法是把登录信息如账号、密码等保存在Cookie中,并控制Cookie的有效期,下次访问时再验证Cookie中的登录信息即可。

    保存登录信息有多种方案。最直接的是把用户名与密码都保持到Cookie中,下次访问时检查Cookie中的用户名与密码,与数据库比较。这是一种比较危险的选择,一般不把密码等重要信息保存到Cookie中

    还有一种方案是把密码加密后保存到Cookie中,下次访问时解密并与数据库比较这种方案略微安全一些。如果不希望保存密码,还可以把登录的时间戳保存到Cookie与数据库中,到时只验证用户名与登录时间戳就可以了。

    这几种方案验证账号时都要查询数据库。

    本例将采用另一种方案,只在登录时查询一次数据库,以后访问验证登录信息时不再查询数据库。实现方式是把账号按照一定的规则加密后,连同账号一块保存到Cookie中。下次访问时只需要判断账号的加密规则是否正确即可本例把账号保存到名为account的Cookie中,把账号连同密钥用MD1算法加密后保存到名为ssid的Cookie中。验证时验证Cookie中的账号与密钥加密后是否与Cookie中的ssid相等。相关代码如下:

    代码1.8 loginCookie.jsp

    <%@ page language="java"pageEncoding="UTF-8" isErrorPage="false" %>

    <%!                                                  // JSP方法

        private static final String KEY =":cookie@helloweenvsfei.com";
                                                         // 密钥 

        public final static String calcMD1(Stringss) { // MD1 加密算法

           String s = ss==null ?"" : ss;                  // 若为null返回空

           char hexDigits[] = { '0','1', '2', '3', '4', '1', '6', '7', '8', '9',
           'a', 'b', 'c', 'd', 'e', 'f' };                        // 字典

           try {

            byte[] strTemp =s.getBytes();                          // 获取字节

            MessageDigestmdTemp = MessageDigest.getInstance("MD1"); // 获取MD1

           mdTemp.update(strTemp);                                // 更新数据

            byte[] md =mdTemp.digest();                        // 加密

            int j =md.length;                                 // 加密后的长度

            char str[] = newchar[j * 2];                       // 新字符串数组

            int k =0;                                         // 计数器k

            for (int i = 0; i< j; i++) {                       // 循环输出

             byte byte0 =md[i];

             str[k++] =hexDigits[byte0 >>> 4 & 0xf];

             str[k++] =hexDigits[byte0 & 0xf];

            }

            return newString(str);                             // 加密后字符串

           } catch (Exception e){return null; }

        }

    %>

    <%

       request.setCharacterEncoding("UTF-8");             // 设置request编码

        response.setCharacterEncoding("UTF-8");        // 设置response编码

       

        String action =request.getParameter("action"); // 获取action参数

       

        if("login".equals(action)){                       // 如果为login动作

            String account =request.getParameter("account");
                                                         // 获取account参数

            String password =request.getParameter("password");
                                                         // 获取password参数

            int timeout = newInteger(request.getParameter("timeout"));
                                                         // 获取timeout参数

                  

            String ssid =calcMD1(account + KEY); // 把账号、密钥使用MD1加密后保存

           

            CookieaccountCookie = new Cookie("account", account);
                                                         // 新建Cookie

           accountCookie.setMaxAge(timeout);              // 设置有效期

           

            Cookie ssidCookie =new Cookie("ssid", ssid);   // 新建Cookie

           ssidCookie.setMaxAge(timeout);                 // 设置有效期

           

           response.addCookie(accountCookie);             // 输出到客户端

           response.addCookie(ssidCookie);            // 输出到客户端

           

            // 重新请求本页面,参数中带有时间戳,禁止浏览器缓存页面内容

           response.sendRedirect(request.getRequestURI() + "?" + System.
            currentTimeMillis());

            return;

        }

        elseif("logout".equals(action)){                  // 如果为logout动作

           

            CookieaccountCookie = new Cookie("account", "");
                                                     // 新建Cookie,内容为空

           accountCookie.setMaxAge(0);                // 设置有效期为0,删除

                  

            Cookie ssidCookie =new Cookie("ssid", ""); // 新建Cookie,内容为空

           ssidCookie.setMaxAge(0);                   // 设置有效期为0,删除

           response.addCookie(accountCookie);         // 输出到客户端

           response.addCookie(ssidCookie);         // 输出到客户端

            //重新请求本页面,参数中带有时间戳,禁止浏览器缓存页面内容

           response.sendRedirect(request.getRequestURI() + "?" + System.
            currentTimeMillis());

            return;

        }

        boolean login = false;                        // 是否登录

        String account = null;                        // 账号

        String ssid = null;                           // SSID标识

       

        if(request.getCookies() !=null){               // 如果Cookie不为空

            for(Cookie cookie :request.getCookies()){  // 遍历Cookie

               if(cookie.getName().equals("account"))  // 如果Cookie名为
                                                        account

                   account = cookie.getValue();       // 保存account内容

               if(cookie.getName().equals("ssid")) // 如果为SSID

                   ssid = cookie.getValue();          // 保存SSID内容

            }

        }

        if(account != null && ssid !=null){    // 如果account、SSID都不为空

            login =ssid.equals(calcMD1(account + KEY));
                                          // 如果加密规则正确, 则视为已经登录

        }

    %>

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN">

           <legend><%= login ? "欢迎您回来" : "请先登录"%></legend>

            <% if(login){%>

                欢迎您, ${ cookie.account.value }. &nbsp;&nbsp;

               <a href="${ pageContext.request.requestURI }?action=logout">
                注销</a>

            <% } else {%>

            <formaction="${ pageContext.request.requestURI }?action=login"
            method="post">

               <table>

                   <tr><td>账号: </td>

                       <td><input type="text"name="account" style="width:
                       200px; "></td>

                   </tr>

                   <tr><td>密码: </td>

                       <td><inputtype="password" name="password"></td>

                   </tr>

                   <tr>

                       <td>有效期: </td>

                       <td><inputtype="radio" name="timeout" value="-1"
                       checked> 关闭浏览器即失效 <br/> <input type="radio"
                       name="timeout" value="<%= 30 *24 * 60 * 60 %>"> 30天
                       内有效 <br/><input type="radio" name="timeout" value=
                       "<%= Integer.MAX_VALUE %>"> 永久有效 <br/> </td> </tr>

                   <tr><td></td>

                       <td><input type="submit"value=" 登  录 " class=
                       "button"></td>

                   </tr>

               </table>

            </form>

            <% } %>

    登录时可以选择登录信息的有效期:关闭浏览器即失效、30天内有效与永久有效。通过设置Cookie的age属性来实现,注意观察代码。运行效果如图1.7所示。


    图1.7  永久登录

    提示:该加密机制中最重要的部分为算法与密钥。由于MD1算法的不可逆性,即使用户知道了账号与加密后的字符串,也不可能解密得到密钥。因此,只要保管好密钥与算法,该机制就是安全的。


    1.2  Session机制

    除了使用Cookie,Web应用程序中还经常使用Session来记录客户端状态。Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力

    1.2.1  什么是Session

    Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。

    如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。

    1.2.2  实现用户登录

    Session对应的类为javax.servlet.http.HttpSession类。每个来访者对应一个Session对象,所有该客户的状态信息都保存在这个Session对象里。Session对象是在客户端第一次请求服务器的时候创建的。Session也是一种key-value的属性对,通过getAttribute(Stringkey)和setAttribute(String key,Objectvalue)方法读写客户状态信息。Servlet里通过request.getSession()方法获取该客户的Session,

    例如:

    HttpSession session = request.getSession();       // 获取Session对象

    session.setAttribute("loginTime", new Date());     // 设置Session中的属性

       

    out.println("登录时间为:" +(Date)session.getAttribute("loginTime"));      // 获取Session属性

    request还可以使用getSession(boolean create)来获取Session。区别是如果该客户的Session不存在,request.getSession()方法会返回null,而getSession(true)会先创建Session再将Session返回。

    Servlet中必须使用request来编程式获取HttpSession对象,而JSP中内置了Session隐藏对象,可以直接使用。如果使用声明了<%@page session="false" %>,则Session隐藏对象不可用。下面的例子使用Session记录客户账号信息。

    源代码如下:

    代码1.9  session.jsp

    <%@ page language="java" pageEncoding="UTF-8"%>

    <jsp:directive.page import="com.helloweenvsfei.sessionWeb.bean.Person"/>

    <jsp:directive.page import="java.text.SimpleDateFormat"/>

    <jsp:directive.page import="java.text.DateFormat"/>

    <jsp:directive.page import="java.util.Date"/>

    <%!

        DateFormat dateFormat = newSimpleDateFormat("yyyy-MM-dd");         // 日期格式化器

    %>

    <%

        response.setCharacterEncoding("UTF-8");        // 设置request编码

        Person[] persons =

        {          

           // 基础数据,保存三个人的信息

            new Person("Liu Jinghua","password1", 34, dateFormat.parse
            ("1982-01-01")),

            new Person("Hello Kitty","hellokitty", 23, dateFormat.parse
            ("1984-02-21")),

            new Person("Garfield", "garfield_pass",23, dateFormat.parse
            ("1994-09-12")),

         };

       

        String message = "";                      // 要显示的消息

       

        if(request.getMethod().equals("POST"))

        {

            // 如果是POST登录       

            for(Person person :persons)

            {          

                // 遍历基础数据,验证账号、密码

                // 如果用户名正确且密码正确

               if(person.getName().equalsIgnoreCase(request.getParameter("username"))&&person.getPassword().equals(request.getParameter("password")))

               {              

                   // 登录成功,设置将用户的信息以及登录时间保存到Session

                   session.setAttribute("person", person);                   // 保存登录的Person

                   session.setAttribute("loginTime", new Date());          // 保存登录的时间              

                   response.sendRedirect(request.getContextPath() + "/welcome.jsp");

                   return;

                }

            }      

            message = "用户名密码不匹配,登录失败。";       // 登录失败

        }

    %>

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN">

    <html>

        // ... HTML代码为一个FORM表单,代码略,请看随书光盘

    </html>


    登录界面验证用户登录信息,如果登录正确,就把用户信息以及登录时间保存进Session,然后转到欢迎页面welcome.jsp。welcome.jsp中从Session中获取信息,并将用户资料显示出来。

    welcome.jsp代码如下:

    代码1.10  welcome.jsp

    <%@ page language="java" pageEncoding="UTF-8"%>

    <jsp:directive.pageimport="com.helloweenvsfei.sessionWeb.bean.Person"/>

    <jsp:directive.page import="java.text.SimpleDateFormat"/>

    <jsp:directive.page import="java.text.DateFormat"/>

    <jsp:directive.page import="java.util.Date"/>

    <%!

        DateFormat dateFormat = newSimpleDateFormat("yyyy-MM-dd");         // 日期格式化器

    %>

    <%

        Person person =(Person)session.getAttribute("person");                       // 获取登录的person

        Date loginTime =(Date)session.getAttribute("loginTime");                     // 获取登录时间

    %>

        // ... 部分HTML代码略

                <table>

                   <tr><td>您的姓名:</td>

                       <td><%= person.getName()%></td>

                   </tr>

                   <tr><td>登录时间:</td>

                       <td><%= loginTime%></td>

                   </tr>

                   <tr><td>您的年龄:</td>

                       <td><%= person.getAge()%></td>

                   </tr>

                   <tr><td>您的生日:</td>

                       <td><%=dateFormat.format(person.getBirthday()) %></td>

                   </tr>

                </table>

    程序运行效果如图1.8所示。


    图1.8  使用Session记录用户信息

    注意程序中Session中直接保存了Person类对象与Date类对象,使用起来要比Cookie方便。

    当多个客户端执行程序时,服务器会保存多个客户端的Session。获取Session的时候也不需要声明获取谁的Session。Session机制决定了当前客户只会获取到自己的Session,而不会获取到别人的Session。各客户的Session也彼此独立,互不可见


    提示Session的使用比Cookie方便,但是过多的Session存储在服务器内存中,会对服务器造成压力。


    1.2.3  Session的生命周期

    Session保存在服务器端。为了获得更高的存取速度,服务器一般把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。因此,Session里的信息应该尽量精简。

    Session在用户第一次访问服务器的时候自动创建。需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session。如果尚未生成Session,也可以使用request.getSession(true)强制生成Session。

    Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,并维护该Session用户每访问服务器一次,无论是否读写Session,服务器都认为该用户的Session“活跃(active)”了一次。


    1.2.4  Session的有效期

    由于会有越来越多的用户访问服务器,因此Session也会越来越多。为防止内存溢出,服务器会把长时间内没有活跃的Session从内存删除。这个时间就是Session的超时时间。如果超过了超时时间没访问过服务器,Session就自动失效了。

    Session的超时时间为maxInactiveInterval属性,可以通过对应的getMaxInactiveInterval()获取,通过setMaxInactiveInterval(longinterval)修改。

    Session的超时时间也可以在web.xml中修改。另外,通过调用Session的invalidate()方法可以使Session失效。


    1.2.5  Session的常用方法

    Session中包括各种方法,使用起来要比Cookie方便得多。Session的常用方法如表1.2所示。

    表1.2  HttpSession的常用方法

    方  法  名

    描    述

    void setAttribute(String attribute, Object value)

    设置Session属性。value参数可以为任何Java Object。通常为Java Bean。value信息不宜过大

    String getAttribute(String attribute)

    返回Session属性

    Enumeration getAttributeNames()

    返回Session中存在的属性名

    void removeAttribute(String attribute)

    移除Session属性

    String getId()

    返回Session的ID。该ID由服务器自动创建,不会重复

    long getCreationTime()

    返回Session的创建日期。返回类型为long,常被转化为Date类型,例如:Date createTime = new Date(session.get CreationTime())

    long getLastAccessedTime()

    返回Session的最后活跃时间。返回类型为long

    int getMaxInactiveInterval()

    返回Session的超时时间。单位为秒。超过该时间没有访问,服务器认为该Session失效

    void setMaxInactiveInterval(int second)

    设置Session的超时时间。单位为秒

    void putValue(String attribute, Object value)

    不推荐的方法。已经被setAttribute(String attribute, Object Value)替代

    Object getValue(String attribute)

    不被推荐的方法。已经被getAttribute(String attr)替代

    boolean isNew()

    返回该Session是否是新创建的

    void invalidate()

    使该Session失效

    Tomcat中Session的默认超时时间为20分钟。通过setMaxInactiveInterval(int seconds)修改超时时间。可以修改web.xml改变Session的默认超时时间。例如修改为60分钟:

    <session-config>

       <session-timeout>60</session-timeout>      <!-- 单位:分钟 -->

    </session-config>


    注意:<session-timeout>参数的单位为分钟,而setMaxInactiveInterval(int s)单位为秒。


    1.2.6  Session对浏览器的要求

    虽然Session保存在服务器,对客户端是透明的,它的正常运行仍然需要客户端浏览器的支持。这是因为Session需要使用Cookie作为识别标志。HTTP协议是无状态的,Session不能依据HTTP连接来判断是否为同一客户,因此服务器向客户端浏览器发送一个名为JSESSIONID的Cookie,它的值为该Session的id(也就是HttpSession.getId()的返回值)。Session依据该Cookie来识别是否为同一用户。

    该Cookie为服务器自动生成的,它的maxAge属性一般为–1,表示仅当前浏览器内有效,并且各浏览器窗口间不共享,关闭浏览器就会失效。

    因此同一机器的两个浏览器窗口访问服务器时,会生成两个不同的Session。但是由浏览器窗口内的链接、脚本等打开的新窗口(也就是说不是双击桌面浏览器图标等打开的窗口)除外。这类子窗口会共享父窗口的Cookie,因此会共享一个Session。


    注意:新开的浏览器窗口会生成新的Session,但子窗口除外。子窗口会共用父窗口的Session。例如,在链接上右击,在弹出的快捷菜单中选择“在新窗口中打开”时,子窗口便可以访问父窗口的Session。

    如果客户端浏览器将Cookie功能禁用,或者不支持Cookie怎么办?例如,绝大多数的手机浏览器都不支持Cookie。Java Web提供了另一种解决方案:URL地址重写。


    1.2.7  URL地址重写

    URL地址重写是对客户端不支持Cookie的解决方案。URL地址重写的原理是将该用户Session的id信息重写到URL地址中。服务器能够解析重写后的URL获取Session的id。这样即使客户端不支持Cookie,也可以使用Session来记录用户状态。HttpServletResponse类提供了encodeURL(Stringurl)实现URL地址重写,例如:

    <td>

        <a href="<%=response.encodeURL("index.jsp?c=1&wd=Java") %>">
        Homepage</a>

    </td>

    该方法会自动判断客户端是否支持Cookie。如果客户端支持Cookie,会将URL原封不动地输出来。如果客户端不支持Cookie,则会将用户Session的id重写到URL中。重写后的输出可能是这样的:

    <td>

        <ahref="index.jsp;jsessionid=0CCD096E7F8D97B0BE608AFDC3E1931E?c=
        1&wd=Java">Homepage</a>

    </td>

    即在文件名的后面,在URL参数的前面添加了字符串“;jsessionid=XXX”。其中XXX为Session的id。分析一下可以知道,增添的jsessionid字符串既不会影响请求的文件名,也不会影响提交的地址栏参数。用户单击这个链接的时候会把Session的id通过URL提交到服务器上,服务器通过解析URL地址获得Session的id。

    如果是页面重定向(Redirection),URL地址重写可以这样写:

    <%

        if(“administrator”.equals(userName))

        {

           response.sendRedirect(response.encodeRedirectURL(“administrator.jsp”));

            return;

        }

    %>

    效果跟response.encodeURL(String url)是一样的:如果客户端支持Cookie,生成原URL地址,如果不支持Cookie,传回重写后的带有jsessionid字符串的地址。

    对于WAP程序,由于大部分的手机浏览器都不支持Cookie,WAP程序都会采用URL地址重写来跟踪用户会话。比如用友集团的移动商街等。


    注意:TOMCAT判断客户端浏览器是否支持Cookie的依据是请求中是否含有Cookie。尽管客户端可能会支持Cookie,但是由于第一次请求时不会携带任何Cookie(因为并无任何Cookie可以携带),URL地址重写后的地址中仍然会带有jsessionid。当第二次访问时服务器已经在浏览器中写入Cookie了,因此URL地址重写后的地址中就不会带有jsessionid了。


    1.2.8  Session中禁止使用Cookie

    既然WAP上大部分的客户浏览器都不支持Cookie,索性禁止Session使用Cookie,统一使用URL地址重写会更好一些。Java Web规范支持通过配置的方式禁用Cookie。下面举例说一下怎样通过配置禁止使用Cookie。

    打开项目sessionWeb的WebRoot目录下的META-INF文件夹(跟WEB-INF文件夹同级,如果没有则创建),打开context.xml(如果没有则创建),编辑内容如下:

    代码1.11 /META-INF/context.xml

    <?xml version='1.0' encoding='UTF-8'?>

    <Context path="/sessionWeb"cookies="false">

    </Context>


    或者修改Tomcat全局的conf/context.xml,修改内容如下:

    代码1.12  context.xml

    <!-- The contents of this file will be loaded for eachweb application -->

    <Context cookies="false">

        <!-- ... 中间代码略 -->

    </Context>

    部署后TOMCAT便不会自动生成名JSESSIONID的Cookie,Session也不会以Cookie为识别标志,而仅仅以重写后的URL地址为识别标志了。


    注意:该配置只是禁止Session使用Cookie作为识别标志,并不能阻止其他的Cookie读写。也就是说服务器不会自动维护名为JSESSIONID的Cookie了,但是程序中仍然可以读写其他的Cookie。



    展开全文
  • session是什么?

    千次阅读 2018-04-08 16:26:42
    目录:1.session 是啥?2.怎么保存的?3.如何运行?4.有生命周期吗?5.关闭浏览器会过期吗?6.Redis代替文件存储session7.分布式session的同步问题session是啥?首先,我大致的知道,session是一次浏览器和服务器的...

    目录:


    1.session 是啥?
    2.怎么保存的?

    3.如何运行?

    4.有生命周期吗?

    5.关闭浏览器会过期吗?

    6.Redis代替文件存储session

    7.分布式session的同步问题


    session是啥?


    首先,我大致的知道,session是一次浏览器和服务器的交互的会话,会话是啥呢?就是我问候你好吗?你回恩很好。就是一次会话,那么对话完成后,这次会话就结束了,还有我也知道,我们可以将一个变量存入全部的$_SESSION['name']中,这样php的各个页面和逻辑都能访问到,所以很轻松的用来判断是否登陆。

    这是我之前理解的session,当然也是对的,只是解释的太肤浅,理解的太表面了,面试官如果听到这样的答案其实是不太满意的。我参考了其他的很多资料,彻底理解清楚session。

    在说session是啥之前,我们先来说说为什么会出现session会话,它出现的机理是什么?我们知道,我们用浏览器打开一个网页,用到的是HTTP协议,学过计算机的应该都知道这个协议,它是无状态的,什么是无状态呢?就是说这一次请求和上一次请求是没有任何关系的,互不认识的,没有关联的。但是这种无状态的的好处是快速。

    所以就会带来一个问题就是,我希望几个请求的页面要有关联,比如:我在www.a.com/login.php里面登陆了,我在www.a.com/index.php 也希望是登陆状态,但是,这是2个不同的页面,也就是2个不同的HTTP请求,这2个HTTP请求是无状态的,也就是无关联的,所以无法单纯的在index.php中读取到它在login.php中已经登陆了!

    那咋搞呢?我不可能这2个页面我都去登陆一遍吧。或者用笨方法这2个页面都去查询数据库,如果有登陆状态,就判断是登陆的了。这种查询数据库的方案虽然可行,但是每次都要去查询数据库不是个事,会造成数据库的压力。

    所以正是这种诉求,这个时候,一个新的客户端存储数据方式出现了:cookie。cookie是把少量的信息存储在用户自己的电脑上,它在一个域名下是一个全局的,只要设置它的存储路径在域名www.a.com下 ,那么当用户用浏览器访问时,php就可以从这个域名的任意页面读取cookie中的信息。所以就很好的解决了我在www.a.com/login.php页面登陆了,我也可以在www.a.com/index.php获取到这个登陆信息了。同时又不用反复去查询数据库。

    虽然这种方案很不错,也很快速方便,但是由于cookie 是存在用户端,而且它本身存储的尺寸大小也有限,最关键是用户可以是可见的,并可以随意的修改,很不安全。那如何又要安全,又可以方便的全局读取信息呢?于是,这个时候,一种新的存储会话机制:session 诞生了。

    我擦,终于把session是怎么诞生的给圆清楚了,不容易啊!!!

    好,session 诞生了,从上面的描述来讲,它就是在一次会话中解决2次HTTP的请求的关联,让它们产生联系,让2两个页面都能读取到找个这个全局的session信息。session信息存在于服务器端,所以也就很好的解决了安全问题。

    session的运行机制和是怎么保存的?


    既然,它也是一种服务区存储数据的方式,肯定也是存在服务器的某个地方了。确实,它存在服务器的/tmp 目录下,这一点我们接下来慢慢讲。

    我们先说下它的运行机制,是怎么分配的。我们主要用PHP中session的机制,其实各种语言都差不多。

    如果这个时候,我们需要用到session,那我们第一步怎么办呢?第一步是开启session:

    session_start();

    这是个无任何返回值的函数,既不会报错,也不会成功。它的作用是开启session,并随机生成一个唯一的32位的session_id,类似于这样:

    4c83638b3b0dbf65583181c2f89168ec
    session的全部机制也是基于这个session_id,它用来区分哪几次请求是一个人发出的。为什么要这样呢?因为HTTP是无状态无关联的,一个页面可能会被成百上千人访问,而且每个人的用户名是不一样的,那么服务器如何区分这次是小王访问的,那次是小名访问的呢?所以就有了找个唯一的session_id 来绑定一个用户。一个用户在一次会话上就是一个session_id,这样成千上万的人访问,服务器也能区分到底是谁在访问了。

    我们做个试验,看看,是不是这样的:

    我们在php.iyangyi.com 域名下的a.php 页面中,输入如下代码:

    session_start(); echo "SID: ".SID."<br>"; echo "session_id(): ".session_id()."<br>"; echo "COOKIE: ".$_COOKIE["PHPSESSID"];

    我们访问一下a.php页面,看能输出什么?
    我们看到居然还有一个警告。我们先一个一个的看。首先SID这个常量,我们没有给它赋值,它居然能有输出,其次session_id()这个系统方法是输出本次生成的session_id。最后$_COOKIE['PHPSESSIID'] 没有值,这个我们接下来说。
    好,我们再次刷新这个页面,我们能看到什么?
    奇怪的事情发生了。SID 没有值了,$_COOKIE['PHPSESSID']中有值了。而且,2次刷新,session_id 都是一样
    的:bjvlo4p38cfqkr1hr7pe924ts3,实际情况下,只要不关闭网页,怎么刷新都是一样:
    既然我们看到COOKIE中有值了,我们,打开firebug开看到底是什么:
    而且这个PHPSESSID的过期时间是会话,什么意思呢?就是浏览器只要不关就一直不存,浏览器一关就过期,消失了。
    好,我们关掉浏览器,重新打开a.php页面,看看有没有什么变化:
    你看,是不是又回到当初第一次打开时候的样子。
    OK,解惑的时候到了:
    每次我们访问一个页面,如果有开启session,也就是有session_start() 时,就会自动生成一个session_id 来标注是这次会话的唯一ID,同时也会自动往cookie里写入一个名字为PHPSESSID的变量,它的值正是session_id,当这次会话没结束,再次访问的时候,服务器会去读取这个PHPSESSID的cookie是否有值有没过期,如果能够读取到,则继续用这个session_id,如果没有,就会新生成一个session_id,同时生成PHPSESSID这个cookie。由于默认生成的这个PHPSESSID cookie是会话,也就是说关闭浏览器就会过期掉,所以,下次重新浏览时,会重新生成一个session_id。
    好,这个是session_id,就用来标识绑定一个用户的,既然session_id生成了。那么当我们往session里面写入数据,是如何保存的,答案是保存在服务器的临时目录里,根据php.ini的配置,我机子上的这个session是存在D:\wamp\tmp 目录里的。我们先说是存在这个目录下,然后待会将如何修改。
    那么它是怎么存的呢?
    同样也是用到session_id。session_id是32位的,服务器会用 sess_前缀 + session_id 的形式存在这个临时目录下,比如上面这个例子:
    所以,每一次生成的session_id都会生成一个这样的文件,用来保存这次会话的session信息。
    我们往session里写入些数据,来看看session是怎么往这个文件里写数据的,我们同样在a.php页面继续加上写入session的语句:
    $_SESSION['hello'] = 123;
    $_SESSION['word'] = 456;
    然后,我刷新页面,由于我并没有关闭页面,就这是说这次会话还没结束,那么肯定还会是同样的session_id : bjvlo4p38cfqkr1hr7pe924ts3

    然后,我们 用编辑器打开它的存储文件sess_bgg20mcl86drbt3j08jg5h5h17这个文件,看看里面是啥?

    hello|i:123;word|i:456;
    
    是序列化的数据,我们肉眼也能读出来。当我们往$_SESSION全局变量里写数据时,它会自动往这个文件里写入。读取session的时候,也会根据session_id 找到这个文件,然后读取需要的session变量。

    这个sess文件不会随着客户端的PHPSESSID过期,也一起过期掉,它会一直存在,出息GC扫描到它过期或者使用session_destroy()函数摧毁,我们在下面讲到session·回收的时候会说到。

    我们大致总结下:

    HTTP请求一个页面后,如果用到开启session,会去读cookie中的PHPSESSID是否有,如果没有,则会新生成一个session_id,先存入cookie中的PHPSESSID中,再生成一个sess_前缀文件。当有写入$_SESSION的时候,就会往sess_文件里序列化写入数据。当读取的session变量的时候,先会读取cookie中的PHPSESSID,获得session_id,然后再去找这个sess_sessionid文件,来获取对应的数据。由于默认的PHPSESSID是临时的会话,在浏览器关闭后,会消失,所以,我们重新访问的时候,会新生成session_id和sess_这个文件。

    好。session生成和保存将清楚了。我们再来看前面提到的几个变量:


    echo "SID: ".SID."<br>";
    
    echo "session_id(): ".session_id()."<br>";
    
    echo "COOKIE: ".$_COOKIE["PHPSESSID"];
    
    SID 是一个系统常量,SID包含着会话名以及会话 ID 的常量,格式为 "name=ID",或者如果会话 ID 已经在适cookie 中设定时则为空字符串,第一次显示的时候输出的是SID的值,当你刷新的时候,因为已经在cookie中存在,所以显示的是一个空字符串。

    session_id() 函数用来返回当前会话的session_id,它会去读取cookie中的name,也就是PHPSESSID值。


    session的相关配置

    上面巴拉巴拉废话说了那么多,应该是可以理解session的一套机制了的,我接下来看看,前面零星的提到了php.ini里面有关于session相关的配置。我们打开php.ini来,搜索session相关,我主要把用到的几个给列出来:


    [Session]
    
    session.save_handler = files
    
    session.save_path = "d:/wamp/tmp"
    
    session.use_cookies = 1
    
    session.name = PHPSESSID
    
    session.auto_start = 0
    
    session.cookie_lifetime = 0
    
    session.serialize_handler = php
    
    session.gc_divisor = 1000
    
    session.gc_probability = 1
    
    session.gc_maxlifetime = 1440
    
    主要我们用到的,常见的大概就是这几个。我们一个一个的说。

    session.save_handler = files 表示的是session的存储方式,默认的是files文件的方式保存,sess_efdsw34534efsdfsfsf3r3wrresa, 保存在 session.save_path = "d:/wamp/tmp" 里,所有这2个都是可配值得。我们上面的例子就是用的这种默认的方式。

    save_handler 不仅仅只能用文件files,还可以用我们常见的memcache 和 redis 来保存。这个我们后面来说。

    session.use_cookies 默认是1,表示会在浏览器里创建值为PHPSESSID的session_id,session.name = PHPSESSID 找个配置就是改这个名字的,你可以改成PHPSB, 那这样就再浏览器里生成名字为PHPSB的session_id 。`(*∩_∩*)′

    session.auto_start = 0 用来是否需要自动开启session,默认是不开启的,所有我们需要在代码中用到session_start();函数开启,如果设置成1,那么session_id 也会自动就生成了。

    session.cookie_lifetime = 0 这个是设置在客户端生成PHPSESSID这个cookie的过期时间,默认是0,也就是关闭浏览器就过期,下次访问,会再次生成一个session_id。所以,如果想关闭浏览器会话后,希望session信息能够保持的时间长一点,可以把这个值设置大一点,单位是秒。

    gc_divisor, gc_probability, gc_maxlifetime 这3个也是配合一起使用,他们是干嘛的呢?他们是干大事情的,回收这些sess_xxxxx 的文件,它是按照这3个参数,组成的比率,来启动GC删除这些过期的sess文件。gc_maxlifetime是sess_xxx文件的过期时间。具体可以参考这个,我觉得他说我比我清楚: session的垃圾回收机制

    session的垃圾回收

    我们通过上面的各种,已经清楚session的种种了,它会产生各种的sess_前缀的文件,久而久之就会形成垃圾数据,而且正常的session读取也会造成压力,所以及时的清理是蛮有用的。

    1. 代码处理

    php代码中有几个函数是用来清理过期的session信息的,主要是这几个:

    unset($_SESSION['hello']);
    session_unset();
    session_destroy();
    
    setcookie(session_name(), '', time()-42000, '/');
    
    unset 这是是常用的销毁标量的方法,不多说,唯一要说的是删除session ,就是将这个sess_xxx的文件的hello变量给删除了,其他的变量该有的都保存着。而 session_unset() 这个不穿参数,这个是销毁sess_xxx文件中的所有变量,但是这个sess_xxx文件还是保存着。而session_destroy 则更狠角了,它是直接将这个sess_xxx文件给删掉。

    一般退出操作里面,我们也会将session_name() 获得到的PHPSESSID也给过期掉,删掉,因为网页没关,不这样删除的话,刷新之后,找个值是存在的,服务器将会重新创建一个一模一样session_id的sess文件。

    2. php gc 自动删除

    php.ini中的几个销毁sess_xxx文件的配置,在上面说了:

    session.gc_divisor = 1000
    session.gc_probability = 1
    session.gc_maxlifetime = 1440
    
    简单说下,其实上面的一个超链接的博客讲的很清楚了,php触发gc删除过期的sess_x的文件的概念是这样计算的:概率= gc_probability/gc_divisor,上面的默认的参数,也就是说概念是1/1000的概念,在页面启动session_start() 函数时候,会触发gc删除过期的sess_文件。这个概率其实是蛮小的

    所以,我们可以将这个概念调整大一点,比如:将gc_probability 也调成1000,那gc_probability/gc_divisor 就等于1了,也就是百分一百会触发。这样就垃圾回收概率就大的多。


    用redis存储session

    上面七七八八说了很多关于session的存储啊机制啊等。现在说说如果用redis 存储session。之前说的都是用文件files存储,现在想用redis,好处有哪些?
    1. 更快的读取和写入速度。redis是直接操纵内存数据的,肯定是要比文件的形式快很多。
    2. 更好的设置好过期时间。文件存储的sess_sdewfrsf文件其实被删除掉还是要考运气的和概率的,很有可能造成sess_文件没即时删除,造成存储磁盘空间过多,和读取SESSION就变慢了。
    3. 更好的分布式同步。设置redis 的主从同步,可以快速的同步session到各台web服务器,比文件存储更加快速。
    总的说来,用redis来存储SESSION速度更快,性能更高。

    要做的第一件事,当然就是安装redis了。具体安装和配置php与redis,就不细说了,可以参考我写的redis相关:redis安装与配置

    redis 安装好了之后,接下来就是修改php.ini了。将原来的files 改成redis:

    session.save_handler = redis
    
    session.save_path = "tcp://127.0.0.1:6381"
    
    需要用到tcp来连接redis,如果你设置reids 有密码访问的话,这样加上就可以了:tcp://127.0.0.1:6381?auth=authpwd

    重启web服务器后,你就可以正常使用SESSION了。和之前使用files存储SESSION一模一样。

    我们看下redis 是怎么存储session的。它是用了有别于文件存储使用sess_前缀的名字,它用PHPREDIS_SESSION: 前缀,再加上session_id 的形式,是一个string 字符串类型,带生存周期的。

    PHPREDIS_SESSION:i9envsatpki9q8kic7m4k68os5



    你会发现,它的值和文件存储session一模一样,都是用php序列化后存储,而且有明确的过期时间,是根据配置:session.gc_maxlifetime = 1440 来设定的,默认1440秒。当然你可以修改成其他的。

    我们写入和读取每页还是一模一样,包括删除和情况,都是一模一样,没有什么变化:

    session_start(); //开启session,如果读不到cookie,会重新生成一个session_id,redis里面也会新生成一个。
    
    echo "SID: ".SID."<br>";
    
    echo "session_id(): ".session_id()."<br>";
    
    echo "COOKIE: ".$_COOKIE["PHPSESSID"];
    
    $_SESSION['hello'] = 123; // 写入session 。会序列化后写入redis中
    $_SESSION['word'] = 456;
    
    var_dump($_SESSION['word']);  //读session。会从redis读到,解序列后,读出这个值。redis 1440秒过期后,将读不到。
    unset($_SESSION['hello']);  // 删除 hello 的session 。会删除 redis的hello值
    session_unset();  // 清空redis 中这个session_id的所有值。
    session_destroy(); // 删除掉这个PHPREDIS_SESSION:i9envsatpki9q8kic7m4k68os5 key。
    
    session同步

    在做了web集群后,你肯定会首先考虑session同步问题,因为通过负载均衡后,同一个IP访问同一个页面会被分配到不同的服务器上,如果不同的服务器用的是不同的reidis服务,那么可能就会出现,一个登录用户,一会是登录状态,一会又不是登录状态。所以session这个时候就要同步了。刚好,我们选择用redis作为了存储,是可以在多台redis 服务器中同步的。

    具体可以搜索 reidis主从同步或者redis 集群



    参考资料:

    http://zhidao.baidu.com/link?url=2_phukSt0xI6SSIVKUE37TxzivLqdCz_JCPhIUPLMB3TX_IWgoVKL2lwDn1Gh7xTykyV3ezU1YQv9s6HD3uhO_

    http://blog.sina.com.cn/s/blog_5f54f0be0100xs7e.html

    http://star0708.blog.163.com/blog/static/181091248201341710100381/

    http://baike.baidu.com/view/25258.htm?fr=aladdin

    http://www.cnblogs.com/hongfei/archive/2012/06/17/2552434.html
    展开全文
  • Session的获取和session存取值

    万次阅读 2018-12-01 11:18:15
    获取session HttpServletRequest request=ServletActionContext.getRequest(); HttpSession session= request.getSession(); 存取session值  存储方式:session.setAttribute("key",value);  ...

    获取session

    HttpServletRequest request=ServletActionContext.getRequest();
    HttpSession session= request.getSession();

    存取session值

     存储方式:session.setAttribute("key",value);
     取值1:session.getAttribute("key");
     取值2:String name=(String)session.getAttribute("key");    
    
    举例如下:
    session.setAttribute("deptname",dname);
    session.getAttribute("deptname");
    
    String dept=(String)session.getAttribute("deptname");  

     

    展开全文
  • Session原理

    万次阅读 多人点赞 2019-07-10 11:16:54
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~...开发工具与关键技术:Java,HTTP协议,session原理 撰写时间:2019-06-17 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~...
  • Session是怎么实现的?存储在哪里?

    万次阅读 多人点赞 2017-06-30 21:19:30
    为什么有session 如何实现session的共享 SESSION 的数据保存在哪里呢 PHP中的session存储 Java中的session存储 博主注前言 文章内容转载或摘录自,如下文章。最后将在文末【博主注】这一部分,指出一些需要注意的...
  • session是什么

    万次阅读 多人点赞 2017-01-03 16:45:41
    今天就来彻底的学一些session是个啥东西,我罗列了几个需要知道的要点: 1.session 是啥? 2.怎么保存的? 3.如何运行? 4.有生命周期吗? 5.关闭浏览器会过期吗? 6.Redis代替文件存储session ...
  • java中session的用法与原理

    万次阅读 2019-05-21 23:05:50
    在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session...
  • session的到底是做什么的?

    万次阅读 多人点赞 2018-02-22 16:20:47
    前言:今天就来彻底的学一些session是个啥东西,我罗列了几个需要知道的要点:1.session 是啥?2.怎么保存的?3.如何运行?4.有生命周期吗?5.关闭浏览器会过期吗?6.Redis代替文件存储session7.分布式session的同步...
  • Session使用方法详解

    万次阅读 2018-06-27 17:20:59
    session_start()函数的语法格式如下: Bool session_start(void) //创建Session,开始一个会话,进行Session初始化 注意:session_start()函数之前不能有任何输出当第一次访问网站时,Seesion_st...
  • 1、为什么有Session和Cookie?. 根据早期的HTTP协议,每次request-reponse时,都要重新建立TCP连接。TCP连接每次都重新建立,所以服务器无法知道上次请求和本次请求是否来自于同一个客户端。因此,HTTP通信是无状态的...
  • Java通过sessionId获取Session

    万次阅读 2014-09-28 00:16:04
    Servlet2.1之后不支持SessionContext里面getSession(String id)方法。 但是,我们可以通过HttpSessionListener监听器和全局静态map自己实现一个SessionContext。 MySessionContext.java: public class ...
  • 设置session超时的三种方式

    万次阅读 2016-06-21 16:42:56
    Tomcat默认session超时时间为30分钟,可以根据需要修改,负数或0为不限制session失效时间。 30 2.在工程的web.xml中设置 15 3.通过Java代码设置 session.setMaxInactiveInterval(30*60);/
  • 关于session的失效时间和过期

    万次阅读 2019-03-07 11:16:11
    Session基本是在我们做项目的时候,使用频率非常高的。 1、session类似于map是键值对的形式存在的。通过session.getAttribute("name");获取对应的name参数信息。 2、2.session的过期时间是从session不...
  • 浅析清除session的几种方法

    万次阅读 2015-10-17 11:06:31
    第一种方法(继承SessionAware类来取得session,然后用invalidate()方法清理) session.removeAttribute("sessionname")是清除SESSION里的某个属性.  session.invalidate()是让SESSION失效.  或许你可以用...
  • jsp页面中获取session中的值

    万次阅读 多人点赞 2015-05-04 00:08:36
    JSTL标签获取Sessionsession.setAttribute("age","123"); ${ sessionScope.age} 在页面上显示的就是123了 sessionScope指的是session的范围,类似还有requestScope,pageScope,contextScope 然后后面的age...
  • 如果没有配置则不会生成session文件,如果配置的目录session.save_path = "E:/ttt"不存在,则会报错: Warning: session_start() [function.session-start]: open(E:/ttt\sess_e0b64760c92422d81c1d6
  • Tp5 session赋值与取值 用法

    万次阅读 2018-06-28 22:52:21
    助手函数赋值 session('name', 'thinkphp'); 模版中取值 {$Request.session.username}
  • sessionid是一个会话的key,浏览器第一次访问服务器会在服务器端生成一个session,有一个sessionid和它对应。tomcat生成的sessionid叫做jsessionid。 session在访问tomcat服务器HttpServletRequest的getSession(true...
  • session.setAttribute("xyyyy",xyyy);保存session.getAttribute("xyyyy");取得把自己要的数据放在session里面传来传去
1 2 3 4 5 ... 20
收藏数 1,325,277
精华内容 530,110
关键字:

session