2020-0507:会话技术&JSP

185 阅读14分钟

今日内容

1.会话技术
    * Cookie
    * Session
2.JSP:入门学习

一、会话技术

1.会话:一次会话保函多次请求和响应。
    * 一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止。
2.功能:在一次会话的范围内的多次请求间,共享数据
    HTTP协议是无状态的,每一对请求和响应和其他请求和响应都是相互独立的,它们之间不能
进行数据的交流。
3.方式:
    1.客户端会话技术:Cookie
        * 把数据存到客户端浏览器。
    2.服务器端会话技术:Session
        * 把数据存到服务器端。

1.1 Cookie : 小饼干

1.概念:客户端会话技术,将数据保存到客户端。
2.快速入门:
    * 使用步骤:
        1.创建Cookie对象,绑定数据
            * new Cookie(String name, String value)
        2.服务器发送Cookie对象给浏览器,浏览器获取Cookie
            * HtttpServletRequest接口中的方法:addCookie(Cookie cookie) 
            * 浏览器拿到带着Cookie的响应后,下一次的请求会自动带着Cookie。
        3.服务器获取Cookie,拿到数据
            * 服务器收到带Cookie的请求,从HttpServletRequest中获取其中的Cookie。
            * Cookie[] getCookies() 
        * 整个过程是针对一个浏览器。

3.实现原理:

        无论是发送Cookie对象也好还是获取Cookie对象也好,最终它们的体现形式都是以HTTP
    协议在进行数据的交互。那么一定是底层通过HTTP协议的请求和响应来完成的。
        首先浏览器发送了第一次请求,请求CookieDemo1的资源。然后服务器会做出响应,响应
    中含有Cookie对象且Cookie对象中绑定这msg=hello这样一对信息。
        response对象会生成一个响应头set-cookie:msg=hello。
        
        浏览器收到一个set-cookie响应头,会自动的将该响应头携带的数据msg=hello保存到
    客户端浏览器本地。并且下一次在此发送请求时会将该数据带着。
        第二次请求时该数据会被放到请求头里面:cookie:msg=hello。服务器可以用封装好的
    API来获取该数据。

4. Cookie的细节
    1.一次可否发送多个Cookie?
        * 可以创建多个Cookie对象,使用多个resp.addCookie()方法发送即可
        
    2.Cookie在浏览器中保存多长时间?
        1.默认情况下,Cookie是存在浏览器内存中,当浏览器关闭后,Cookie数据被销毁。
        
        2.设置Cookie的生命周期,使之持久化存储:Cookie对象中的方法
            * setMaxAge(int seconds)
                1.正数:将Cookie数据写到硬盘的文件中,持久化存储。Cookie的存活时间。
                        如果传参数30,意味着30s后Cookie将自动删除。
                2.负数:默认值,存在浏览器内存中,当浏览器关闭后,Cookie数据被销毁。
                3.0:表示删除Cookie信息。
                
    3.Cookie是存字符串的,new Cookie(String name, String value),那么能否存中文呢?
        * 在tomcat8之前 cookie不能直接存储中文数据
            * 需要将中文数据转码--一般采用URL编码。
        * tomcat8之后,可以直接存储中文数据。
        * 但是特殊字符还是不支持:比如[space]空格(ASCII--32),建议使用URL编码存储,
          URL解码解析。
        
    4.Cookie在同一个服务器下,不同的web项目下能否共享?
        1. 假设在一个tomcat服务器中,部署了多个web项目,那么在这些web中cookie能否共享?
            * 默认情况下不能共享。
            * setPath(String path):设置cookie的获取功能。
              默认情况下,设置的是当前项目下的虚拟目录。
              ```
                Cookie c1 = new Cookie("msg", "你好");
                c1.setPath("/day0507_cookie")
                resp.addCookie(c1);
              ```
              意味着只有在day0507_cookie项目下的资源才能够获取cookie。
            * 如果要共享:c1.setPath("/"):
              意味着在当前的服务器的根路径下面的所有项目都可以访问这个cookie。  
        2.不同的tomcat服务器间cookie能否共享?
            * setDomain(String path):如果设置一级域名相同,那么多个服务器之间cookie
              可以共享。
            * 如:setDomain(".baidu.com"),那么tieba.baidu.com和news.baidu.com中的cookie
              可以共享。
    
    5.Cookie的特点和作用
        1.cookie存储在客户端浏览器(没有那么安全)
        2.浏览器对于单个Cookie的大小(4kb)有限制,以及对同一个域名下的总cookie数量(20)
          也有限制。
        * 作用:
            1.cookie一般用于存储少量的不太敏感的数据。
            2.在不登录的情况下,完成服务器对客户端的身份识别。
                  把你对百度首页进行的设置以cookie的形式存到浏览器中,那么下一次你再访问
                百度首页(因为是带着这个cookie访问),百度服务器就可以拿到这个cookie信息,
                来完成你上一次对它的设置。
                
    6.案例:记住上一次访问时间
        1.案例需求:
        	1. 访问一个Servlet,如果是第一次访问,则提示:您好,欢迎您首次访问。
        	2. 如果不是第一次访问,则提示:欢迎回来,您上次访问时间为:显示时间字符串
        2.分析:
            1.可以采用cookie完成
            2.在服务器中的servlet判断是否有一个名为lastTime的cookie
                1.有:不是第一次访问
                    * 响应数据:欢迎回来,您上次访问时间为:2020年xx月xx日xx:xx:xx
                    * 写会cookie:lastTime-2020年xx月xx日xx:xx:xx
                2.没有:是第一次访问
                    * 响应数据:您好,欢迎您首次访问。
                    * 写会cookie:lastTime-2020年xx月xx日xx:xx:xx
                    
        * 详见CookieTest

二、JSP

1.概念:
    * Java Server Pages: java服务器端页面
        * 可以理解为:一个特殊的页面,其中既可以直接定义html标签,又可以定义java代码。
        * 用于简化书写的。
            展示一个页面一般会有动态的资源和静态的资源,动态的资源时java代码生成的,
        静态资源时html/css/js等标签元素显示的。但是在一个Servlet资源中,该如何展示显示
        这些资源?我们只能用java代码,resp.getWrite().wirte()往页面上去响应这些动态的数
        据和静态的标签。效果如下:
            ```
            resp.getWrite().wirte("
            <html>
              <head>
                <title>$Title$</title>
              </head>
              <body>
                <h1>hi~ jsp</h1>
              </body>
            </html>
            ")
            ```
            显然这里如果页面复杂的话,要写一大堆,非常的麻烦。所以如果有一个页面静态资
        源可以在里面直接写标签,动态资源可以直接写java代码。两种语言在该页面都可以直接
        定义。那么这种页面就有了应用的范围。
            可以大大减少程序员开发的复杂度。所以JSP就是用于简化书写的。
            
2.原理
    * JSP本质上就是一个Servlet

    * 当服务器中有一个JSP的页面index.jsp,我们通过浏览器来请求这个资源页面,请求的路径:
localhost/day0507_cookie_session/index.jsp。
    * 服务器收到这个请求会做出这些事情:
        1.首先服务器解析请求消息:找是否有index.jsp资源。(没有:404)
        2.找到后,会将index.jsp转成index.java文件
        3.编译index.java文件,生成index.class文件
        4.由index.class来提供访问。
    * 真正上是由index.class为客户端做出响应。
    * 思考:这个字节码文件index.class能够被浏览器访问到,那么这个字节码文件是什么?
        一定是一个Servlet,只有Servlet才能被外界访问到。一个java类要想被外界访问到,那
    么必须是一个Servlet。
        所以:JSP本质上就是一个Servlet
        
    * 验证JSP本质上就是一个Servlet:
        打开项目的配置目录:
            C:\Users\13099\.IntelliJIdea2019.3\system\tomcat\Tomcat_8_5_31_day0507_cookie&session_2
            在最开始该目录下没有work文件夹。
            
        访问index.jsp页面:
            发现在该目录下自动生成一个work文件夹,这个目录和在tomcat目录下的work目录
            一样。放我们运行时产生的文件。
            
        打开work\Catalina\localhost\day0507_cookie_session\org\apache\jsp:
            里面有两个文件:
            index_jsp.class
            index_jsp.java
            就是index.jsp对应生成的java/class文件。
            
        那么这个index_jsp.java是不是一个Servlet呢?打开index_jsp.java:
            public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
            这个类继承了:org.apache.jasper.runtime.HttpJspBase
            查看tomcat源码:
                public abstract class HttpJspBase extends HttpServlet
                发现HttpJspBase extends HttpServlet
                
        * 所以可以验证JSP本质上就是一个Servlet
            
3. JSP的脚本:JSP定义java代码的方式
    ```
    <%
      System.out.println("hello jsp");
    %>
    ```
    java代码必须写在<% %>里面,否则不能生效,这个区域就称为JSP脚本。
    1.<% 代码 %> :定义的java代码在index_jsp.java中的jspService()方法中。jspService()
                   方法中可以定义什么,该脚本中就可以定义什么。
    2.<%! 代码 %>:定义的java代码在index_jsp.java类中的成员位置。(成员变量/代码块)
    3.<%= 代码 %>:定义的java代码会输出到页面上,输出语句中可以定义什么,该脚本就可以
      定义什么。
      
     ```
         <%
          System.out.println("hello jsp");
          int i = 5;
        %>
        <%!
          int i = 3;
        %>
        <%=
          i
        %>
     ```
    对应到:index_jsp.java类中的位置
    ```
        public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase{
            int i = 3;
            public void _jspService{
                System.out.println("hello jsp");
                int i = 5;
            }
        }
    ```

4. JSP的内置对象:
    * 在Jsp页面中不需要获取和创建,可以直接使用的对象
        ```
        <%
          System.out.println("hello jsp");
          int i = 5;
          String contextPath = request.getContextPath();
          out.print(contextPath);
        %>
        ```
        如:request和out对象我没有获取就可以直接拿来用,这就被称为内置对象。
    * 为什么能这样使用呢?
        因为这些代码都会放到index_jsp.java类的public void _jspService{}中执行。
        只要_jspService{}方法中对这些对象有申明那么_jspService{}方法就可以使用:
  public void _jspService(final javax.servlet.http.HttpServletRequest request, 
  final javax.servlet.http.HttpServletResponse response){
    javax.servlet.jsp.JspWriter out = null;    
  }
        显然request/response/out对象都在该方法中声明了,所以是JSP的内置对象可以直接使用。
    * JSP一共有9个内置对象。
    * 今天学习三个:
        request
        response
        out:可以将数据输出到页面上(字符输出流对象)。和response.getWrite()类似
        
        * response.getWrite()和out.write()的区别:
            out对象定义在哪个位置就会在那个位置输出。
            response.getWrite().write()先于out.write()输出。
            因为两个流都有缓冲区,将来真正给客户端浏览器作相应之前,tomcat会先找response
        的缓冲区,再去找out的缓冲。所以response缓冲区的数据永远在out的缓冲区之前输出。
            建议out输出,因为index_jsp.java文件中都是out输出,如果用response可能会打乱布局。

三、Session: 主菜

1.概念:服务器端会话技术,在一次会话的多次请求响应间来共享数据的。将数据保存到服务器端。HttpSession
2.快速入门:request,servetContext都可以共享数据,都是域对象。
    HttpSession对象中有这几个个方法
        Object getAttribute(String name)  
        void setAttribute(String name, Object value)  
        void removeAttribute(String name)  
    * 步骤:
        1.获取HttpSession对象:
            HttpSession session = request.getSession();
        2.使用HttpSession对象:
            Object getAttribute(String name)  
            void setAttribute(String name, Object value)  
            void removeAttribute(String name)
    * 结论:
        HttpSession对象可以在一会的多次请求间共享数据,也只能在一次会话的多次请求间
        共享数据。
        
3.Session的原理
    ```
    public class SessionDemo1 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //1.获取session对象
            HttpSession session = request.getSession();
            //2.存储数据
            session.setAttribute("msg", "hello session");
        }
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    
    public class SessionDemo2 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //1.获取session对象
            HttpSession session = request.getSession();
            //2.获取数据
            Object msg = session.getAttribute("msg");
            System.out.println(msg);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request,response);
        }
    }
    ```
    在SessionDemo1中种Session存储数据SessionDemo2中获取Session的数据,而且在两个类中
都有获取Session的操作:HttpSession session = request.getSession()。
    这两次获取的Session的是同一个,那么服务器是怎么保证在一次会话范围呢,多次获取的
Session对象是同一个呢?
    
    具体原理: Session是依赖Cookie的,而Cookie是依赖请求头和响应头的。
        1.浏览器发送http://localhost/day0507_cookie_session/sessionDemo1,请求
          SessionDemo1资源。
        2.在SessionDemo1第一次获取Session时是没有Cookie。服务器会在内存中创建一个新的
          Session对象,并且有一个唯一的id=xxxxxx。
        3.服务器作响应:发送一个响应头set-cookie:JSESSION=xxxxxx
        4.浏览器收到响应:会将cookie信息:set-cookie:JSESSION=xxxxxx存到浏览器本地。
        5.浏览器在下次请求服务器时:请求中会带着一个请求头:cookie:JSESSION=xxxxxx。
          然后服务器获取cookie信息去查找内存中是否有这样一个session对象,发现存在后,第二次再获取的session对象就是同一个session。
          
* 结论:Session的实现时依赖于Cookie的。

4.细节:
    1.当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
        不是同一个,关闭后意味着会话结束,cookie被销毁。
            在打开浏览器请求服务器时不带有请求头cookie:JSESSION=xxxxxx。
        如果需要相同:则自己手动创建一个cookie在设置最大存活时间,让cookie持久化存储。
            ```
                Cookie c = new Cookie("JSESSIONID", session.getId());
                c.setMaxAge(60*60);
                response.addCookie(c);
            ```
    2.客户端不关闭,服务器关闭后,两次获取的session是同一个吗?
        * 不是同一个
        org.apache.catalina.session.StandardSessionFacade@28e0c37
        org.apache.catalina.session.StandardSessionFacade@1572dcf1
        服务器关闭后,发现两次获取的session是不同的。
        * 尽管两次的session对象的地址值是不一样的,但是却可以做到里面的确保数据不丢失。
            * session的钝化:序列化
                * 在服务器正常关闭之前,将session对象系列化到硬盘上
            * session的活化:反序列化
                * 在服务器启动后,将session文件转化为内存中的session对象即可。
                
        * 2.1 IDEA不会帮我们做上面两步,但是tomcat帮我们做了。 
                所以我们将本地的java项目:day0507_cookie_session打成war包放到tomcat的
            安装目录webapps中。
                然后在bin目录中运行startup.bat启动tomcat后,tomcat就会自动部署war包
            中的项目。
                浏览器访问http://localhost:8080/day0507_cookie_session/sessionDemo1和
            http://localhost:8080/day0507_cookie_session/sessionDemo2。这里没有设置端
            口所以是8080。
                session被序列化后的文件也放到了work文件夹中。打开work/day0507_cookie_
            session文件夹此时里面什么都没有。
            
                正常关闭tomcat后,发现work/day0507_cookie_文件夹中生成一个SESSIONS.ser
            文件。这里面放的就是session对象。
            ------->这就是session的钝化:序列化
                再打开tomcat:这个SESSIONS.ser文件将会被服务器自动读取,然后删掉该文件。
            ------->session的活化:反序列化
        结论:所以tomcat通过session的钝化和活化帮我们保存了session对象中的数据。即使
              是服务器关闭前后的session。
              
        
        * 2.2 IDEA当前服务器的配置文件在:
            C:\Users\13099\.IntelliJIdea2019.3\system\tomcat\Tomcat_8_5_31_day0507_cookie
        &session
            IDEA在关闭服务时也会进行session的钝化,将SESSIONS.ser放到该目录下的work
        目录中。
            但是在IDEA再重新启动服务器时,IDEA会将work目录删掉,然后再创建一个新的work
        目录,此时新的work目录下已经没有了SESSIONS.ser文件了。
    
        * 结论IDEA不能实现session的活化。但是我们将来不会再IDEA本地部署项目,而是在tomcat服务器里面,放到webapps目录下面。
            
    3。session什么时候被销毁
        1. 服务器关闭
        2. HttpSession中有一个方法:
            invalidate()方法
        3. session有一个默认的失效时间:30min
           可以在web.xml中修改:
            <session-config>
                <session-timeout>30</session-timeout>
            </session-config>
            
5. session的特点
    1. session用于存储一次会话的多次请求的数据,存在服务器端
       一次会话间的数据可以用session共享。会话域
    2. session可以存储任意类型,任意大小的数据。cookie只能是字符串,而且大小有限制。
    
    * session和cookie的区别
        1. session存储数据在服务器端,cookie在客户端
        2. session没有数据大小限制,cookie有
        3. session数据较为安全,cookie相对不安全。

四、案例

案例:验证码
1. 案例需求:
	1. 访问带有验证码的登录页面login.jsp
	2. 用户输入用户名,密码以及验证码。
		* 如果用户名和密码输入有误,跳转登录页面,提示:用户名或密码错误
		* 如果验证码输入有误,跳转登录页面,提示:验证码错误
		* 如果全部输入正确,则跳转到主页success.jsp,显示:用户名,欢迎您
2. 分析:

3. 代码实现