Java Web编程

一.Servlet

    1.RequestDispatcher

       RequestDispatcher是一个接口,它包含两个方法:forward(request, response)和

       include(request, response)。RequestDispatcher 有一个特点,就是浏览器上显示

       的URL是最先请求的目标资源的URL,不会因为使用了forward、include方法而改

       变。因此forward和include的调用对于用户来说是透明的。

     (1)forward

               这个方法将请求从一个 Servlet or JSP目标资源 上 转发到服务器上的另一个

               资源(servlet、JSP 文件或 HTML 文件,这些资源必须是当前Web上下文中

               的),让其它的资源去生成响应数据。如下图中的,用户请求的是目标资源

               servlet1,servlet1接受到请求后,转发到servlet2,真正产生响应数据是

               servlet2,而servlet1只是起个引导转发作用。浏览器的地址栏不会变,依然

               是servlet1的URL。 

                

               注意点:

             (a)在目标资源中调用forward方法时,必须保证此响应没有提交。也就是不

                      要使用 ServletResponse 对象的输出流对象,因为即便你写入了数据到

                      响应缓冲区,最后也会被清空,如果缓冲区数据被刷新提交(out.flush),

                      还会抛出IllegalStateException异常。

             (b)对于forward方法传递的request对象:虽然我们从调用上看,好像是将

                      request对象传递给转动的资源上去了,但是我发现目标资源使用的

                      request对象和转发的资源使用的request对象不是同一个request对象,

                      因为分别从这2个request中获取RequestURL,发现是不一样的。但是

                      在目标资源request提取的Paramter 和 Attribute   ,在转发后的资源的

                      request对象中,依然都可以提取到,且是相同的。所以,二者只是在

                      请求路径相关的属性上不同,其它API调用返回的都是一样的。

             (c)在forward语句的前后,都不应该有响应输出的语句,应该会被忽略。

               ServletRequest接口的getRequestDispatcher()方法返回

               RequestDispatcher的对象。句法:

RequestDispatcher requestDispatcher = request.getRequestDispatcher("/sessionLoginDemo/login.jsp");//得到转发器


requestDispatcher.forward(request, response);//转发(调度)请求给/sessionLoginDemo/login.jsp 并由login.jsp发送response给客户端。

     (2)include

              此方法用于包含响应中某个资源(servlet、JSP 页面和 HTML 文件)的内

              容。调用者指定一个被包含的资源,将这个包含的资源(JSP,Servlet,

              HTML)的响应数据包含到自己的响应体中。被包含的数据是在服务器上经

              过运行产生的,因此是动态包含,而不同于JSP中的include指令,它是

              JSP转译期的静态包含。这个过程实质是用一个相同的Request再请求

              一次被包含的资源,将被包含的资源的响应数据包含到原本的资源中去,

              构成它的响应数据的一部分。

              

    2.response.sendRedirect

       示例:response.sendRedirect("login.jsp");

      使用response.sendRedirect传递参数:

response.sendRedirect("/UsersManager/MainFrame?uname="+username+"&pwd="+password);

    3.RequestDispatcher.forward和response.sendRedirect的区别:

     (1)response.sendRedirect相当于浏览器接收到了响应之后又向服务器发送

              了一次请求,所以相当于两次请求。RequestDispatcher.forward相当于

              方法调用,在执行当前文件的过程中转向执行目标文件,两个文件(当

              前文件和目标文件)属于同一次请求,最本质的特点就是两次请求共享

              了reques对象和response对象。

     (2)地址栏不同:response.sendRedirect方式下用户在浏览器地址栏中看

              到的是目标文件的地址,RequestDispatcher.forward方式下用户在浏览

              器地址栏中看到的是当前文件的地址。

    4.java web的四大作用域

     (1)PageContext域:作用范围是整个JSP页面,是四大作用域中最小的一

                                          个;生命周期是当对JSP的请求时开始,当响应结束

                                          时销毁。

     (2)ServletRequest域:作用范围是整个请求链(请求转发也存在);生命

                                              周期是在service方法调用前由服务器创建,传入

                                              service方法。整个请求结束,request生命结束。

                                              线程安全

     (3)HttpSession域:作用范围是一次会话。生命周期是在第一次调用

                                         request.getSession()方法时,服务器会检查是否已经

                                         有对应的session,如果没有就在内存中创建一个

                                         session并返回。当一段时间内session没有被使用

                                       (默认为30分钟),则服务器会销毁该session。如果

                                         服务器非正常关闭(强行关闭),没有到期的session

                                         也会跟着销毁。如果调用session提供的invalidate(),

                                         可以立即销毁session。线程不安全

                                         session销毁的方式:

                                       (a)超时(一般服务器设置超时时间为30分钟)服务器会销毁

                                                session;

                                       (b)点击控制台的红色按钮异常关闭服务器要销毁session

                                       (c)手动调用session的invalidate方法session.invalidate();

                                         注意:服务器正常关闭不销毁session,()session会存到我

                                                    们的硬盘中,也就是我们正常的点击stop server()会在

                                                    tomcat的work的Catalina\localhost\项目名称下面生成

                                                    一个文件SESSIONS(执行序列化),当服务器再次启动

                                                    的时候会加载此文件(反序列化),倘若没有实现序列化

                                                    接口(Serializable)可能会报错因为序列化和反序列化会

                                                    依据一个id。

                                         问:浏览器关闭后session会销毁吗?

                                         答:不会。

                                                我们知道Session是存在于服务器端的,当把浏览器关闭

                                                时,浏览器并没有向服务器发送任何请求来关闭Session,

                                                自然Session也不会被销毁,但是可以做一点努力,在所

                                                有的客户端页面里使用js的window.onclose来监视浏览器

                                                的关闭动作,然后向服务器发送一个请求来关闭Session,

                                                但是这种做法在实际的开发中也是不推荐使用的,最正常

                                                的办法。

                                         问:那么为什么当我们关闭浏览器后,就再也访问不到之前的

                                                session了呢?

                                         答:其实之前的Session一直都在服务器端,而当我们关闭浏

                                                览器时,此时的Cookie是存在于浏览器的进程中的,当浏

                                                览器关闭时,Cookie也就不存在了。其实Cookie有两种:

                                                一种是存在于浏览器的进程中;一种是存在于硬盘上。而

                                                session的Cookie是存在于浏览器的进程中,那么这种

                                                Cookie我们称为会话Cookie,当我们重新打开浏览器窗口

                                                时,之前的Cookie中存放的Sessionid已经不存在了,此时

                                                服务器从HttpServletRequest对象中没有检查到sessionid,

                                                服务器会再发送一个新的存有Sessionid的Cookie到客户端

                                                的浏览器中,此时对应的是一个新的会话,而服务器上原先

                                                的session等到它的默认时间到之后,便会自动销毁。就是

                                                不去管它,让它等到默认的时间后,自动销毁。

                                                注:当在同一个浏览器中同时打开多个标签,发送同一个请

                                                       求或不同的请求,仍是同一个session;当不在同一个窗口

                                                       中打开相同的浏览器时,发送请求,仍是同一个session;

                                                       当使用不同的浏览器时,发送请求,即使发送相同的请

                                                       求,是不同的session;当把当前某个浏览器的窗口全关闭,

                                                       再打开,发起相同的请求时,就是本文所阐述的,是不同

                                                       的session,但是它和session的生命周期是没有关系的。

     (4)ServletContext(application)域:作用范围是整个Web应用。当Web应用

                                                                     被加载进容器时创建代表整个web应用

                                                                     的ServletContext对象,当服务器关闭或

                                                                     Web应用被移除时,ServletContext对象

                                                                     跟着销毁。线程不安全

     关于作用域的线程安全的详细内容,查看:

     http://www.itkeyword.com/doc/903165062469129x507/Web-ServletContex-SessionRequest

    6.web应用中的对象

     (1)Servlet

              Servlet通常称为服务器端小程序,是运行在服务器端的程序,用于处理及响

              应客户的请求。Servlet是个特殊的java类,继承于HttpServlet。客户端通常

              只有GET和POST两种请求方式,Servlet为了响应则两种请求,必须重写

              doGet()和doPost()方法。大部分时候,Servlet对于所有的请求响应都是完全

              一样的,此时只需要重写service()方法即可响应客户端的所有请求。

            (a)HttpServlet有三个方法:

                     init(ServletConfig config):创建Servlet实例时,调用该方法的初始化

                                                                Servlet资源。在 Servlet 的生命期中,仅

                                                                执行一次 init()方法。它是在服务器装入

                                                                Servlet 时执行的。可以对它进行覆盖。

                     service() 方法:service() 方法是 Servlet 的核心。每当一个客户请求一

                                              个HttpServlet对象,该对象的service() 方法就要被调用,

                                              而且传递给这个方法一个"请求"(ServletRequest)对象和

                                              一个"响应"(ServletResponse)对象作为参数。 在

                                              HttpServlet中已存在service() 方法。缺省的服务功能是

                                              调用与 HTTP 请求的方法相应的 do 功能。例如, 如果

                                              HTTP 请求方法为 GET,则缺省情况下调用 doGet() ;

                                              HTTP请求方法为POST,则缺省情况下调用doPost()。

                                              Servlet 应该为 Servlet 支持的 HTTP方法覆盖 do 功能。

                                              因为 HttpServlet.service() 方法会检查请求方法是否调用

                                              了适当的处理方法,不必要覆盖 service() 方法。只需覆

                                              盖相应的 do 方法就可以了。

                     destroy() 方法:destroy() 方法仅执行一次,即在服务器停止且卸装

                                               Servlet时执行该方法。典型的,将 Servlet 作为服务器

                                               进程的一部分来关闭。可以对它进行覆盖。

            (b)Servlet的响应

                     Servlet的响应可以是下列几种类型: 

                     一个输出流,浏览器根据它的内容类型(如text/HTML)进行解释。 

                     一个HTTP错误响应, 重定向到另一个URL、servlet、JSP。 

            (c)Servlet的生命周期

                   (i)创建Servlet实例。

                           创建Servlet实例有两个时机:

                                  第一个时机:客户端第一次请求某个Servlet时,系统创建该

                                                        Servlet的实例。

                                  第二个时机:Web应用启动时立即创建Servlet实例,即

                                                        load-on-start Servlet。

                           load-on-start:

                                   load-on-startup标记容器是否在启动的时候实例化并调用其

                                   init()方法的优先级。它的值表示  servlet应该被载入的顺序。

                                   当值为0或者大于0时,表示容器在应用启动时就加载并初始

                                   化这个servlet。如果值小于0或未指定时,则表示只有在第一

                                   次请求的容器才在该servlet调用初始化函数。正值越小

                                   servlet的优先级越高,应用启动时就越先加载。值相同时,

                                   容器就会自己选择顺序来加载。

                   (ii)Web容器调用Servlet的init()方法,对Servlet进行初始化。

                   (iii)Servlet初始化后,将一直存在于容器中,用于响应客户端请求,如

                            果客户端发送GET请求,容器调用Servlet的doGet()方法处理并响应

                            请求;如果客户端发送POST请求,容器调用Servlet的doPost()方法

                            处理并响应请求。或者统一使用service()方法处理来响应用户请求。

                   (iv)Web容器决定销毁Servlet时,先调用Servlet的destory()方法,通常

                             在关闭Web应用时销毁Servlet实例。

            (d)Servlet的线程安全

                   (i)对象分为有状态的对象和无状态的对象

                           有状态的对象:有数据存储功能,即有实例变量;

                           无状态的对象:无数据存储功能,即无实例变量。

                     Servlet是一个单例模式,是否线程安全即取决于它的实现是有状态的

                     Servlet还是无状态的Servlet。传统的实现方式,即实现HttpServlet为

                     有状态的Servlet,所以非线程安全,它的线程安全问题如下:

                            静态变量:非线程安全

                            实例变量:非线程安全

                            局部变量:线程安全

                     如果Servlet是实现SingleThreadModel接口,它就是无状态的,即线

                     程安全。

     (1)ServletContext(application)

              官方叫servlet上下文,ServletContext是一个web应用的上下文,服务器会为

              每一个工程创建一个对象,这个对象就是ServletContext对象。这个对象全局

              唯一,而且工程内部的所有servlet都共享这个对象。所以叫全局应用程序共

              享对象。它是一个全局信息的存储空间,代表当前web应用。

            (a)创建和销毁

                     ServletContext在web应用(服务器)启动时创建,ServletContext在Web

                     应用(服务器)关闭时释放。

            (b)如何获取ServletContext

                   (i)调用ServletConfig类的getServletContext()方法

                           在init(ServletConfig config)方法中,使用

                           ServletContext context=config.getServletContext();

                   (ii)调用GenericServlet类的getServletContext()方法

                            其实这种方式也是调用ServletConfig类的getServletContext()方法,

                            因为GenericServlet类实现了ServletConfig接口;

                   (iii)调用HttpSession类的getServletContext()方法

                   (iv)ServletContextEvent类只有一个方法,就是getServletContext()

     (2)ServletContextListener和ServletContextEvent

              当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent 事件,该

              事件由ServletContextListener 来处理。在 ServletContextListener 接口中定义

              了处理ServletContextEvent 事件的两个方法。

/**
 * 当Servlet 容器启动Web 应用时调用该方法。在调用完该方法之后,容器再对Filter 初始化,
 * 并且对那些在Web 应用启动时就需要被初始化的Servlet 进行初始化。
 */
contextInitialized(ServletContextEvent sce) 


/**
 * 当Servlet 容器终止Web 应用时调用该方法。在调用该方法之前,容器会先销毁所有的Servlet 和Filter 过滤器。
 */
contextDestroyed(ServletContextEvent sce)

              下面展示三个例子:

              例一,在服务启动时,将数据库中的数据加载进内存,并将其赋值给一个属性

                         名,其它的 Servlet 就可以通过 getAttribute 进行属性值的访问。包括两

                         个步骤:

                        (i)实现 servletContextListerner 接口 并将要共享的通过

                                setAttribute ( name,data )方法提交到内存中去;

                        (ii)应用项目通过 getAttribute(name) 将数据取到 。

                         Java代码:

public class ServletContextLTest implements ServletContextListener{ 

    // 实现其中的销毁函数
    
    public void contextDestroyed(ServletContextEvent sce) { 

        System.out.println("this is last destroyeed");    

    } 

    // 实现其中的初始化函数,当有事件发生时即触发

    public void contextInitialized(ServletContextEvent sce) { 

        ServletContext sct=sce.getServletContext(); 

        Map<Integer,String> depts=new HashMap<Integer,String>(); 

        Connection connection=null; 

        PreparedStatement pstm=null; 

        ResultSet rs=null; 

         

        try{ 

            connection=ConnectTool.getConnection(); 

            String sql="select deptNo,dname from dept"; 

            pstm=connection.prepareStatement(sql); 

            rs=pstm.executeQuery(); 

            while(rs.next()){ 

                depts.put(rs.getInt(1), rs.getString(2)); 

            } 

            // 将所取到的值存放到一个属性键值对中

            sct.setAttribute("dept", depts); 

            System.out.println("======listener test is beginning========="); 

        }catch(Exception e){ 

            e.printStackTrace(); 

        }finally{ 

            ConnectTool.releasersc(rs, pstm, connection); 

        } 

    } 

}

                         在完成上述编码后,仍需在 web.xml 中进行如下配置,以使得该监听器可以起作用。                         

                         web.xml代码:

<listener>  
   <listener-class>ServletContextTest.ServletContextLTest</listener-class> 
</listener>

                         在完成上述配置后, web 服务器在启动时,会直接加载该监听器,通过以下的应用

                         程序就可以进行数据的访问。

              例二,启动线程

public class DSAction extends Thread implements ServletContextListener {

    public void contextInitialized(ServletContextEvent arg0) {
        
        super.start();// 启动一个线程
    }
    public void zdfs() throws IOException {

        Huoquzhuye u = new Huoquzhuye();// 爬虫方法类
        Htmlneirong h = new Htmlneirong();// 存入数据库类
        List<String> list = u.seturl("http://xxxxxxx");
        for (int i = 0; i < list.size(); i++) {
            String txt = list.get(i).substring(0, 22);
            String start = list.get(i).substring(4, 14);
            String end = list.get(i).substring(22, list.get(i).length());
            try {
                h.seturl(txt, start, end);
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (SQLException e) {

                e.printStackTrace();
            }
        }

    }

    @Override
    public void run() {
        while (true) {
            try {
                this.zdfs();
                super.sleep(1000 * 60 * 10);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.
     * ServletContextEvent)
     */


    /*
     * (non-Javadoc)
     * 
     * @see
     * javax.servlet.ServletContextListener#contextInitialized(javax.servlet
     * .ServletContextEvent)
     */

    public void contextDestroyed(ServletContextEvent arg0) {
        super.stop();// 停止线程

    }
}

                         web.xml代码

<listener>
   <listener-class>bj.hbj.dingshi.DSAction</listener-class>
</listener>

              (i)调用super.start()开启线程。

              (ii)最后关闭线程super.stop()。

    7.web.xml

     (1)web.xml的加载顺序:

              ServletContext -> context-param(无顺序)-> listener(无顺序)-> filter(书写顺序)

              -> servlet(load-on-startup优先级)

              详细步骤如下:

            (i)启动一个web项目,web容器(如tomcat)读取web.xml文件,读取其中的配置信

                    息。

            (ii)容器创建一个servlet上下文(servletContext),这个web项目所有部分共享这个

                    上下文。

            (iii)容器将<context-param>转换为键值对,交给servletContext

            (iv)容器创建<listener>中的监听器实例

            (v)触发contextInitialized方法,listener被调用(当Servlet 容器启动或终止Web 应

                     用时,会触发ServletContextEvent 事件,该事件由ServletContextListener 来

                     处理。在 ServletContextListener 接口中定义了处理ServletContextEvent 事件

                     的两个方法contextInitialized和contextDestroyed,web.xml有

                     contextLoaderListener监听器,spring等框架实现了本监听器的接口方法)调

                     用完contextInitialized方法后,容器再对filter初始化。

                     ContextLoaderListener:Spring实现的类,它继承自ContextLoader,并且实

                                                             现ServletContextListener接口(实现了

                                                             contextInitialized和contextDestroyed,这是它的核

                                                             心功能)。详细查看:

                                                             https://blog.csdn.net/qq_15037231/article/details/78743765

            (vi)容器对web.xml中的指定load-on-startup的值为正数Servlet初始化

                    (优先级1,2,3...->递减),负数或不指定则在该Servlet调用时初始化

                      (springMVC的初始化为此阶段)

     (2)web.xml文件中配置<context-param>和<init-param>的区别

              <context-param>和<init-param>都是上下文参数,但它们的范围和使用方式不同。

              <context-param>是application范围内的初始化参数,用于向servlet-context提供键

              值对,即应用程序的上下文信息,listener、filter等初始化时会用到这些信息。

              <init-param>是servlet范围内的参数,只能在servlet类的init()方法中取得。

              示例如下:

<context-param>  
           <param-name>context/param</param-name>  
           <param-value>avalible during application</param-value>  
</context-param>
 <servlet>
    <servlet-name>ReadContext</servlet-name>
    <servlet-class>file.ReadContext</servlet-class>
    <init-param>
        <param-name>user1</param-name>
        <param-value>user1-ps</param-value>
    </init-param>
  </servlet>

     (3)<session-config></session-config>

              <session-config> 用于设置容器的session参数,比如:<session-timeout> 用于指

              定http session的失效时间,-1 代表session永远不会过期。

     (4)<listener></listener>

             (a)listener介绍

                      <listener>为web应用程序定义监听器,监听器用来监听各种事件,比如:

                      application和session事件,所有的监听器按照相同的方式定义,功能取决去

                      它们各自实现的接口,常用的Web事件接口有如下几个:

                    (i)ServletContextListener:用于监听Web应用的启动和关闭;

                    (ii)ServletContextAttributeListener:用于监听ServletContext范围

                            (application)内属性的改变;

                    (iii)ServletRequestListener:用于监听用户的请求;

                    (iv)ServletRequestAttributeListener:用于监听ServletRequest范围

                            (request)内属性的改变;

                    (v)HttpSessionListener:用于监听用户session的开始和结束;

                    (vi)HttpSessionAttributeListener:用于监听HttpSession范围(session)

                              内属性的改变。

                      <listener>主要用于监听Web应用事件,其中有两个比较重要的WEB应用

                      事件:应用的启动和停止(starting up or shutting down)和Session的创

                      建和失效(created or destroyed)。应用启动事件发生在应用第一次被

                      Servlet容器装载和启动的时候;停止事件发生在Web应用停止的时候。

                      Session创建事件发生在每次一个新的session创建的时候,类似地

                      Session失效事件发生在每次一个Session失效的时候。为了使用这些

                      Web应用事件做些有用的事情,我们必须创建和使用一些特殊的“监听

                      类”。它们是实现了以下两个接口中任何一个接口的简单java类:

                      javax.servlet.ServletContextListener或

                      javax.servlet.http.HttpSessionListener,如果想让你的类监听应用的启动

                      和停止事件,你就得实现ServletContextListener接口;想让你的类去监

                      听Session的创建和失效事件,那你就得实现HttpSessionListener接口。

             (b)Listener配置

                      配置Listener只要向Web应用注册Listener实现类即可,无序配置参数之

                      类的东西,因为Listener获取的是Web应用ServletContext(application)

                      的配置参数。为Web应用配置Listener的两种方式:

                    (i)使用@WebListener修饰Listener实现类即可。

                    (ii)在web.xml文档中使用<listener>进行配置。

                      注意:6中的(2)是只针对ServletContextEvent配置的一个Listener。

                      web.xml的方式如下所示:


<listener>  
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
</listener>

     (5)<filter></filter>

              

二.Jsp

    1.Forward和Redirect

       Forward和Redirect代表了两种请求转发方式:直接转发和间接转发。

       直接转发方式(Forward):客户端和浏览器只发出一次请求,Servlet、

                                                     HTML、JSP或其它信息资源,由第二个信息资

                                                     源响应该请求,在请求对象request中,保存的

                                                     对象对于每个信息资源是共享的。

       间接转发方式(Redirect):实际是两次HTTP请求,服务器端在响应第一

                                                     次请求的时候,让浏览器再向另外一个URL发

                                                     出请求,从而达到转发的目的。

    2.Jsp的九个隐藏变量

     (1)Request

     (2)Response

     (3)Session

     (4)application

     (5)out

     (6)config

              javax.servlet.ServletConfig实例化对象,我们也可以在web.xml文件中配置

              JSP,只是很少用。

     (7)page

              翻译成Servlet后相当于this

     (8)PageContext

              参看:http://www.cnblogs.com/fjdingsd/p/5117303.html

     (9)exception 

              exception 对象的作用是显示异常信息,只有在包含 isErrorPage="true" 的页

              面中才可以被使用,在一般的JSP页面中使用该对象将无法编译JSP文件。

              excepation对象和Java的所有对象一样,都具有系统提供的继承结构。

              exception 对象几乎定义了所有异常情况。在Java程序中,可以使用try/catch

              关键字来处理异常情况; 如果在JSP页面中出现没有捕获到的异常,就会生

              成 exception 对象,并把 exception 对象传送到在page指令中设定的错误页

              面中,然后在错误页面中处理相应的 exception 对象。

    3.<%@include%>和<jsp:include>的区别

      <%@include file="文件的URL"%> **静态引入**

      页面请求之前预编译,所有代码包含进来之后,一起进行处理,把所有代码合在

      一起,编译成一个servlet

      <jsp:include page="文件的URL"/>**动态引入**

      所有代码分别处理,在页面被请求的时候才编译,被编译成多个servlet,页面语

      法相对独立,处理完成之后再将代码的显示结果(处理结果)组合进来。

三.Web应用的编码问题

    1.先了解字符集和编码

    (1)字符集和编码

             概念:简单的说字符集就规定了某个文字对应的二进制数字存放方式

                      (编码)和某串二进制数值代表了哪个文字(解 码)的转换关系

             我们在计算机屏幕上看到的是实体化的文字,而在计算机存储介质中存放的

             实际是二进制的比特流。那 么在这两者之间的转换规则就需要一个统一的标

             准,否则把我们的U盘插到老板的电脑上,文档就乱码了;小伙伴QQ上传过

             来的文件,在我们本地打开又乱码了。 于是为了实现转换标准,各种字符集

             标准就出现了。

             例如,“屌”这个字,对应的编码如下:  

字符集 16进制编码 对应的二进制数据
UTF-8 0xE5B18C 1110 0101 1011 0001 1000 1100
UTF-16 0x5C4C 1011 1000 1001 1000
GBK 0x8CC5 1000 1100 1100 0101

           (a)ASCII

                    ASCII 码使用指定的 7 位或 8 位二进制数组合来表示 128 或 256 种可能

                    的字符。标准 ASCII 码也叫基础ASCII码,使用 7 位二进制数来表示所有

                    的大写和小写字母,数字 0 到 9、标点符号, 以及在美式英语中使用的

                    特殊控制字符。

           (b)非ASCII编码

                    英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够

                    的。比如,在法语中,字母上方有注音符号,它就无法用 ASCII 码表示。

                    于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比

                    如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲

                    国家使用的编码体系,可以表示最多256个符号。但是,这里又出现了新

                    的问题。不同的国家有不同的字母,因此,哪怕它们都使用256个符号的

                    编码方式,代表的字母却不一样。比如,130在法语编码中代表了é,在希

                    伯来语编码中却代表了字母Gimel (ג),在俄语编码中又会代表另一个符号。

                    但是不管怎样,所有这些编码方式中,0--127表示的符号是一样的,不一

                    样的只是128--255的这一段。至于亚洲国家的文字,使用的符号就更多了,

                    汉字就多达10万左右。一个字节只能表示256种符号,肯定是不够的,就必

                    须使用多个字节表达一个符号。比如,简体中文常见的编码方式是GB2312,

                    使用两个字节表示一个汉字,所以理论上最多可以表示 256 x 256 = 65536

                    个符号。

           (c)UNICODE

                    正如上一节所说,世界上存在着多种编码方式,同一个二进制数字可以被

                    解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码

                    方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常

                    出现乱码?就是因为发信人和收信人使用的编码方式不一样。以想象,如

                    果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个

                    独一无二的编码,那么乱码问题就会消失。这就是 Unicode,就像它的名

                    字都表示的,这是一种所有符号的编码。Unicode 当然是一个很大的集合,

                    现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,

                    U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表

                    示汉字严。具体的符号对应表,可以查询unicode.org,或者专门的汉字对

                    应表。

                    Unicode的问题:

                            需要注意的是,Unicode 只是一个符号集,它只规定了符号的二进制

                    代码,却没有规定这个二进制代码应该如何存储。

                    比如,汉字严的 Unicode 是十六进制数4E25,转换成二进制数足足有15

                    位(100111000100101),也就是说,这个符号的表示至少需要2个字节。

                    表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。这里就

                    有两个严重的问题,第一个问题是,如何才能区别 Unicode 和 ASCII ?

                    计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第

                    二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果

                    Unicode 统一规定,每个符号用三个或四个字节表示,那么每个英文字母

                    前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件

                    的大小会因此大出二三倍,这是无法接受的。它们造成的结果是:一是出

                    现了 Unicode 的多种存储方式,也就是说有许多种不同的二进制格式,可

                    以用来表示 Unicode。二是Unicode 在很长一段时间内无法推广,直到互

                    联网的出现。

                  (i)UTF-8

                           互联网的普及,强烈要求出现一种统一的编码方式。UTF-8 就是在互

                           联网上使用最广的一种 Unicode 的实现方式。其他实现方式还包括

                           UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个

                           字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,

                           UTF-8 是 Unicode 的实现方式之一。UTF-8 最大的一个特点,就是它

                           是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不

                           同的符号而变化字节长度。UTF-8 的编码规则很简单,只有二条:

                           第一条:对于单字节的符号,字节的第一位设为0,后面7位为这个符

                                         号的Unicode码。因此对于英语字母,UTF-8 编码和 ASCII

                                         码是相同的。

                          第二条:对于n字节的符号(n > 1),第一个字节的前n位都设为1,第

                                         n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提

                                         及的二进制位,全部为这个符号的 Unicode 码。下表总结了编

                                         码规则,字母x表示可用编码的位。

                          总结:

                          UNICODE和UTF-8的区别:

                               UNICODE是字符集(编码),UTF-8是编码格式,编码格式是用来序列化或

                               存储字符集编码的一种“格式”。。

                          关于UNICODE和UTF-8参考自:

                          https://blog.csdn.net/Deft_MKJing/article/details/79460485

                  (ii)UTF-16

                           暂时略

                  (iii)UTF-32

                            暂时略

    3.关于windows平台的记事本编码说明

    具体参看:http://www.cnblogs.com/WestGarden/archive/2012/09/02/3138331.html

    4.Java中的编码

       首先强调一下“编码和编码格式”(这里有些和上面的内容重复)

       Unicode是一种“编码”,所谓编码就是一个编号(数字)到字符的一种映射关系,就仅仅是一

       种一对一的映射而已;GBK、UTF-8是一种“编码格式”,是用来序列化或存储1中提到的那

       个“编号(数字)”的一种“格式”。

       GBK和UTF-8都是用来序列化或存储Unicode编码的数据的,但是分别是2种不同的格式,

       他们都是Unicode编码的实现方式;他们俩除了格式不一样之外,他们所关心的Unicode编

       码范围也不一样。UTF-8考虑了很多种不同国家的字符;而GBK只考虑中文——在

       Unicode中的一小部分的字符的编码。

      (1)J2SE中的编码

               java的String使用的编码是Unicode,当String存在于内存中时(在代码中用string类型

               的引用对它进行操作时),是"只有编码而没有编码格式的",所以java程序中的任何

               String对象,说它是gbk还是utf-8都是错的,String在内存中不需要“编码格式”,它只

               是一个Unicode的字符串而已。

               当字符串需要在网络中传输或要被写入文件时,就需要使用编码格式了。乱码问题

               也因此出现。

             (a)Java文件编译后形成class

                      这里Java文件的编码可能有多种多样,但Java编译器会自动将这些编码按照

                      Java文件的编码格式正确读取后产生class文件,这里的class文件编码是

                      Unicode编码(具体说是UTF-16编码)。

                      因此,在Java代码中定义一个字符串:String s="汉字";不管在编译前java文件

                      使用何种编码,在编译后成class后,他们都是一样的----Unicode编码表示。

             (b)JVM中的编码

                      JVM加载class文件读取时候使用Unicode编码方式正确读取class文件,那么

                      原来定义的String s="汉字";在内存中的表现形式是Unicode

      (2)Java Web中的编码

               

               假定浏览器就是ie,WEB服务器是tomcat,页面是jsp,应用服务器用的是servlet

             (a)浏览器中打开了一个页面,这个页面映射应用服务的某个jsp。此时浏览器中

                      的解码格式是什么呢?

                      浏览器的解码格式是在jsp中指定的,比如在jsp文件中经常可以看到这样两

                      行代码。

<!-- 
  这一句是和Tomcat说的:保存在硬盘上的jsp文件在被Tomcat翻译成servlet的时候,使用utf-8解码jsp文件的内容。
  如果不指定,会默认使用iso-8859-1来解码。
-->
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 


<!-- 这一行是和浏览器说的:浏览器解码的时候请使用utf-8解码哦 -->
<meta http-equiv="content-type" content="text/html; charset=UTF-8">

                      这样浏览在解析这个jsp的时候,就是使用utf-8编码来解码。

                      浏览器的解码格式也是可以在浏览器上设置的:

                       

             (b)response中也可以设置内容的编码格式和指定浏览器的解码格式。

                      开发人员应该都知道,jsp就是一个特殊一点的servlet。在servlet中, response

                      有两个设置编码的方法,和jsp中的那两行编码配置有着相似的功能。

        // 表示response的内容会以utf-8的编码方式编码后发送给浏览器。
     response.setCharacterEncoding("UTF-8");

      // 告诉浏览器,解码的时候也要使用utf-8解码哦。
        response.setContentType("text/html;charset=UTF-8");

             (c)通过request对象可以指定应用服务用哪种编码格式来解码接收到的数据

    // 通过这句话,可以指定用utf-8编码格式来解码浏览器传来的数据。不过只对post方式传来的数据有效。如果是get方法传来的数据,还是会以默认的iso-8859-1来解码。
    request.setCharacterEncoding("UTF-8");

             (d)设置tomcat服务器配置文件server.xml,指定以何种编码解码浏览器传来的参

                      数。

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" URIEncoding="UTF-8" />

                

                     'URIEncoding="UTF-8"', 这个属性的配置, 和"

                     request.setCharacterEncoding("UTF-8");"这句代码的功能大致相同。都是指

                     定tomcat服务器接受到收据后如何解码。如果不指定,tomcat服务器会默认

                     使用iso-8859-1来解码。如果一个tomcat服务器里有多个应用,应该考虑该处

                     修改是否会对其它应用造成伤害。

             (e)get和post传中文

                     (i)get

                             方法一:在客户端使用 URLEncoder.encode(“中文”,”UTF-8”)对中文参

                                           数进行编码,在服务器端需要进行解码

                                           this.setName(java.net.URLDecoder.decode(name, “UTF-8”)); 

                             方法二:修改tomcat的server.xml文件

                                      

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8" useBodyEncodingForURI="true"/>

                             

                             其它和web中乱码的解决方案差不多。

                     (ii)post

                              和web中乱码的解决方案差不多。

                     乱码问题的解决来源网络,是否好使,不知道,需要自己实验一下,实验暂

                     时略。

    5.URL重写和重定向(这是两个比较古老的技术)

     (1)URL重写

            (a)概念

              URL 重写是拦截客户端传入 Web 请求URL并自动将其定向到到规则指定的 URL

              的过程。比如浏览器发来请求 http://blog.mocoder.com/hello.html ,服务器自动

            (b)URL重写的好处

                   (i)搜索引擎比较喜欢.html,.htm的(与.jsp,.php,.aspx,.cff相比),因为.html,

                           .htm是静态的,更容易让引擎了解你网页的内容。而动态网页的内容是根

                           据用户,来输出不同的内容,不容易让引擎吸收具体HTML内容。

                   (ii)如果不用URL Rewriting将拓展名隐藏或改成.html,那么假如这个网站要

                            换个技术或把动态页面换成静态,则需要寻找所有含有拓展名的连接,

                            把连接所含URL进行拓展名修改(如从JSP换到PHP技术,则要寻找所有

                            含有.jsp的页面,并把所有含.jsp的URL改成.php,费时费力)。

                            URL Rewriting正好避免了这点,因为好的URL是能做到“不变应完变”的。

                   (iii)防止某些黑客恶意攻击。

                            有些大网站采用不同的技术开发不同功能的页面。而把拓展名改掉,让黑

                            客无法确认此页面用的技术是什么,从而就无从下手。

                   (iv)方便访问者使用。访问者不是程序员,他们不明白什么是.jsp,.php.aspx,

                             他们只知道URL。所以统一把拓展名拿掉,或者同意把拓展名换为html,

                             htm,有利于用户的使用。用户可以知道现在在你网站的位置,如何通过

                             输入URL到某一页面。

                     url重写在php开发的web程序中应用十分广泛,当然,大多数的javaweb框架

                     如springMVC、struts都有指定访问url的配置,但是不够灵活。

     (2)URL重定向

              URL重定向,是指当使用者浏览某个网址时,将他导向到另一个网址的技术。

     (3)重写和重定向的区别

              重定向 :浏览器知道页面位置发生变化,从而改变地址栏显示的地址。

                             搜索引擎意识到页面被移动了,从而更新搜索引擎索引,将原来失效的

                             链接从搜索结果中移除。

                             临时重定向(R=302)和永久重定向(R=301)都是亲搜索引擎的,是SEO

                             的重要技术

              重写:用于将页面映射到本站另一页面,若重写到另一网络主机(域名),则按

                         重定向处理。

                         浏览器的URL地址不会发生变化。

四:Web应用的安全

       1.CSS攻击,跨站脚本攻击

          跨站脚本,顾名思义,就是恶意攻击者利用网站漏洞往Web页面里插入恶意代码,

          一般需要以下几个条件:

        (a)客户端访问的网站是一个有漏洞的网站,但是他没有意识到;

        (b)在这个网站中通过一些手段放入一段可以执行的代码,吸引客户执行(通过鼠标

                 点击等);

        (c)客户点击后,代码执行,可以达到攻击目的。

          解决方案:对用户输入的数据进行HTML转移处理。如今很多开源框架默认就支持

                            HTML的转义。

       2.CSRF攻击(Cross Site Request Forgery, 跨站域请求伪造)

          你这可以这么理解 CSRF 攻击:攻击者盗用了你的身份,伪装成你发送恶意请求。

          CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购

          买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。

          具体攻击过程如下:

        (1)用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;

        (2)在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登

                 录网站A成功,可以正常发送请求到网站A;

        (3)用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;

        (4)网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第

                 三方站点A

          解决方案:

        (1)将cookie设置成HttpOnly

        (2)增加token

                 其原理是在请求中放入攻击者所不能伪造的信息,并且该新信息不存在于cookie

                 中。鉴于此,系统开发人员可以在HTTP请求中以参数的形式加入一个随机产生

                 的token,并在服务端进行token验证,如果请求中没有token或者token内容不正

                 确,则认为CSRF攻击而拒绝该请求。例如,token可以存放在表单中的隐藏域

                 中:<input type="hidden" name="_token" value="tokenvalue"/>。token的值通

                 过服务端生成,表单提交后token的值通过POST请求与参数一同带到服务端,

                 每次回话可以使用相同的token,会话过期,则token失效,攻击者因无法获取

                 token,也就无法伪造请求。

                 

HttpSession session=request.getSession()
Object token=session.getAttribute("_token")
if(token==null||"".equals(token))
{
    session.setAttribute("_token",UUID.randomUUID().toString());
}

       3.SQL注入攻击

          解决方案:

        (1)使用预编译语句(PreparedStatement)

                 预编译语句使用参数占位符来替代需要动态传入的参数,这样攻击者无法改变

                 SQL语句的结构,SQL语句的语义不会发生变化,即便用户输入sql,它会将其

                 转义。

                 例如,原语句:

                 select * from hhuser where nick=nickname and password=password

                 例如用户输入 'or '1'='1时,不使用预编译,生成语句为:

                 select * from hhuser where nick='zhangsan' and password='' or '1'='1'

                 使用预编译:        

                 select * from hhuser where nick='zhangsan' and password='\' or \'1\'=\'1'

                 可见发生了转义。

        (2)使用ORM框架,如IBATIS和Hibernate等都支持输入变量的转义

                 实现办法时通过#配置变量

       4.文件上传漏洞

        (1)一般处理方法

                 为了防止用户上传恶意的可执行文件和脚本,以及将文件服务器当做免费的文

                 件存储服务器使用,我们需要对上传的文件进行白名单校验并限制上传文件的

                 大小,上传文件需要重新命名,使共攻者无法猜测上传文件的访问路径。对于

                 上传的文件来说,不能简单地通过后缀名称判断文件的类型,因为恶意攻击可

                 以将可执行文件的后缀名称改成图片或者其它后缀类型,诱导用户执行。因

                 此,判断文件类型需要使用更安全的方式,很多类型的文件起使得几个字节内

                 容使固定得,根据这几个字节的内容,就可以确定文件的类型,这几个字节被

                 称为魔数。

        (2)配合使用imagemaglck

                 对于图片类型的文件,可以上传后对图片进行相应的缩放,破坏恶意用户上传

                 的二进制可执行文件的结构。imagemaglck是一套功能强大、稳定并且开源的

                 对图片进行处理的开发工具包,能处理多种格式的图片文件,可以利用

                 imagemaglck来对图片进行缩放。

                 魔数枚举类型:

public enum FileType {

    /** JPEG */
    JPEG("FFD8FF"),

    /** PNG */
    PNG("89504E47"),

    /** GIF */
    GIF("47494638"),

    /** TIFF */
    TIFF("49492A00"),

    /** Windows bitmap */
    BMP("424D"),

    /** CAD */
    DWG("41433130"),

    /** Adobe photoshop */
    PSD("38425053"),

    /** Rich Text Format */
    RTF("7B5C727466"),

    /** XML */
    XML("3C3F786D6C"),

    /** HTML */
    HTML("68746D6C3E"),

    /** Outlook Express */
    DBX("CFAD12FEC5FD746F "),

    /** Outlook */
    PST("2142444E"),

    /** doc;xls;dot;ppt;xla;ppa;pps;pot;msi;sdw;db */
    OLE2("0xD0CF11E0A1B11AE1"),

    /** Microsoft Word/Excel */
    XLS_DOC("D0CF11E0"),

    /** Microsoft Access */
    MDB("5374616E64617264204A"),

    /** Word Perfect */
    WPB("FF575043"),

    /** Postscript */
    EPS_PS("252150532D41646F6265"),

    /** Adobe Acrobat */
    PDF("255044462D312E"),

    /** Windows Password */
    PWL("E3828596"),

    /** ZIP Archive */
    ZIP("504B0304"),

    /** ARAR Archive */
    RAR("52617221"),

    /** WAVE */
    WAV("57415645"),

    /** AVI */
    AVI("41564920"),

    /** Real Audio */
    RAM("2E7261FD"),

    /** Real Media */
    RM("2E524D46"),

    /** Quicktime */
    MOV("6D6F6F76"),

    /** Windows Media */
    ASF("3026B2758E66CF11"),

    /** MIDI */
    MID("4D546864");

    private String value = "";

    private FileType(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

}

                 

          

猜你喜欢

转载自blog.csdn.net/jialanshun/article/details/83793813