JavaWeb

目录
    1 Socket
    2 软件结构
    3 Servlet
    4 HTTP
    5 单点登录SSO
    6 常见问题总结

参考资料
    · 《深入分析 Java Web》

1 Socket

1.1 概念

    套接字(socket),是描述计算机之间完成相互网络通信的抽象功能,没有对应的实体。通讯的两端都有Sokcet,数据在两个Sokcet间通过IO传输。

    格式为:IP:端口号

1.2 分类

    · TCP和UDP

    · 两种方式传输数据都是通过序列化java对象后,通过二进制协议传输,故Socket通信和编程语言没有关系。

2 软件结构

2.1 C/S体系

    Client-Server 客户端-服务器端。属于桌面应用程序。
    · 弊端:
        ·需要安装软件
        ·维护难,占空间
        ·服务端升级,客户端也需要升级
    · 优点:
        ·体验效果好
        ·占宽带小
        ·减轻服务器端压力

2.2 B/S体系

    Browser-Server浏览器端-服务器端,属于网站应用程序。
    · 弊端:
        ·需要使用浏览器访问
        ·兼容性差、安全性差
        ·服务器端压力大
    · 优点:
        ·不需要特定客户端
        ·服务端升级,浏览器不需要升级。

3 Servlet

3.1 资源的分类

    · 静态资源:当用户多次访问这个资源,资源的源代码永远不会改变的资源。

    · 动态资源:当用户多次访问这个资源,资源的源代码可能会发送改变。

3.2 实质

    Servlet本质是Server Applet 服务连接器。是服务器端的程序,具有独立平台和协议的特性,用于交互式地浏览和生成数据,生成动态Web内容

3.3 使用

    编写类继承HttpServlet类,并覆盖doGet和doPost方法,并在web.xml文件或者使用@WebServlet注解配置访问路径。

public class HelloServlet extends HttpServlet{

    @Override

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //解决中文乱码问题

       resp.setCharacterEncoding("utf-8");//内容编码,防止出现中文乱码

        resp.setContentType("text/html;charset=utf-8"); //向浏览器输出内容

        resp.getWriter().write("这是第一个servlet程序。当前时间为:"+new Date());

    }

}

3.4 工作原理

3.4.1 Servlet容器

    servlet容器的产生是为了技术上的解耦,通过标准化接口来相互协作。    

图3-1 Tomcat容器

    图中看出,真正管理servlet的容器是Context容器,且一个Context对应一个Web工程。

3.4.2 Servlet容器的启动

Tomcat启动代码

Tomcat tomcat=getTomcatInstance();
File appDir= new File(getBuildDirectory(), "webapps/examples") ;
tomcat. addWebapp(null, "/examplesappDir. getAbsolutePath());
tomcat. start();
ByteChunk res = geturl( "http://localhost:"+getport()+" /examples/servlets/servlet/HelloWorldExample");
assertTrue(res tostring().indexOf(

Hello World!

")>0);

public Context addwebapp (Hosthost, String url, String path){
    silence(url);
    Context ctx  = new StandardContext();
     ctx.setPath( url);
     ctx. setDocBase(path);
     if(defaultRealm== null){
         initsimpleAuth();
    }
     ctx. setRealm(defaultRealm);
     ctx. addLifecycleListener(new DefaultWebXmlListener());
     Contextconfig ctxCfg = new ContextConfig();
     ctx. addLifecycleListener(ctxCfg);
     ctxcfg. setDefaultwebXml ("org/apache/catalin/startup/DEFAULT_XMI ") ;
     if(host == null){
         getHost(). addchild (ctx);
     } else{
         host.addchild(ctx);
     }
     return ctx;

(1)Tomcat启动过程

        ·getTomcatInstance()获取Tomcat实例
        ·新建StandardContext()容器,并设置访问URl和项目文件访问地址
        ·添加LifecycleListener
        ·新建ContextConfig。解析Web应用的配置文件web.xml等
        ·tomcat实例执行addWebapp()
        ·tomcat实例执行start(),启动整个Tomcat容器。

(2)tomcat所有的容器都集成Lifecycle接口,Lifecycle接口管理着容器的整个生命周期,所有容器的修改和状态的改变都会由它去通知已经注册的观察者(Listener)。这是基于观察者模式设计的。

(3)Context容器的启动

        在tomcat容器启动后,当Context容器处于init初始化状态时,其中的Listener将会被调用。

        首先执行ContextConfig的Init方法:
            · 创建用于解析XML配置文件的contextDigester对象
            · 读取默认的 context. xm配置文件,如果存在则解析它。
            · 读取默认的Hos配置文件,如果存在则解析它
            · 读取默认的 Context自身的配置文件,如果存在则解析它
            · 设置 Context的 DocBase

        ContextConfig的init方法完成后, Context容器就会执行startInternal()方法,这个方法的启动逻辑比较复杂,主要包括如下几部分:
            · 创建读取资源文件的对象
            · 创建 ClassLoader对象
            · 设置应用的工作目录
            · 启动相关的辅助类,如 logger、 realm、 resources等。
            · 修改启动状态,通知感兴趣的观察者(Web应用的配置)
            · 子容器的初始化
            · 获取 ServletContext并设置必要的参数。
            · 初始化“ load on startup”的 Servlet. 其他Servlet在第一次被调用的时候初始化。

        在初始化时,会将Servlet包装成StandardWrapper。因为StandardWrapper是Tomcat容器中的一部分,它具有容器的特性,而Servlet作为一个独立的Web开发标准,不应该强耦合在Tomcat中。

3.4.3 Servlet实例的建立和初始化

(1)创建实例

        web.xml中的配置项,“load-on-startup”如果大于0,则在Tomcat启动时Servlet就会被启动。调用Wrapper.loadServlet方法获取servletClass,并交给InstanceManager去创建对象。

(2)初始化实例

        调用StandardWrapper的InitServlet方法初始化对象。

3.4.4 Servlet体系结构

    · ServletConfig,在Servlet初始化时就传到Servlet,以StandardWrapperFacade对象的形式调用,可以防止暴露不必要的数据。

    · ServletContext,获取Context容器的信息。

    · ServletRquest

    · ServletResponse

    体系设计过程中用到门面设计模式

3.4.5 Servlet调用

(1)访问URL:http://hostname:port/contextpath/servletPath

(2)Tomcat中的org.apache.tomcat.util.http.mapper完成URL到一个Servlet子容器的映射工作。Mapper类保存了Tomcat的Container容器中的所有子容器的信息,根据传入Servlet容器的请求的hostname和contextpath设置到request的mappingData属性中。

(3)Mapper子类MapperListener作为监听这监听容器的变化,这样其中的mapper属性也相应修改。

(4)执行Servlet接口service(ServletRequest req, ServletResponse resp)方法。

3.4.6 Filter

3.4.6.1 简介

    Filter也称之为过滤器,它是 Servlet 技术中最实用的技术,Web 开发人员通过 Filter 技术,对 web 服务器管理的所有 web 资源:例如 Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现 URL 级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

    它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用 Filter 的完整流程:Filter 对用户请求进行预处理,接着将请求交给 Servlet 进行处理并生成响应,最后 Filter 再对服务器响应进行后处理。

3.4.6.2 Demo

/**

*  使用Filter 打印参数

* @author Administrator

*

*/

public class FilterDemo implements Filter {

    public  FilterDemo(){

         System.out.println("FilterDemo 构造函数被执行...");

    }

/**

* 销毁

*/

    public void destroy() {

        System.out.println("destroy");

    }

/*
    用户在每个请求进来时,访问doFilter方法,在Servlet的service方法之前调用。

*/

    public void doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse,

                FilterChain paramFilterChain) throws IOException, ServletException {

        System.out.println("doFilter");

        HttpServletRequest request = (HttpServletRequest) paramServletRequest;

        HttpServletResponse response = (HttpServletResponse) paramServletResponse;

        // 请求地址

        String requestURI = request.getRequestURI();

        System.out.println("requestURI:"+requestURI);

        // 参数

        Map parameterMap = request.getParameterMap();

        for (String key : parameterMap.keySet()) {

        String[] arr=parameterMap.get(key);

        }

    }

/**

* 初始化

*/

    public void init(FilterConfig paramFilterConfig) throws ServletException {

        System.out.println("init");

    }

}

  FilterDemo

  com.qian.servlet.FilterDemo

    FilterDemo

    /*

3.4.6.3 相关类

(1)FilterConfig

        可以通过其获取ServletContext对象。

(2)FilterChain

        责任链设计模式。FilterChain是doFilter方法的传入参数,保存了当前整个请求链。通过调用FilterChain.doFilter方法,可以将请求继续传递下去,但是如果要拦截请求,则不调用。

        当FilterChain上所有的Filter对象执行完成后,才会执行最终的Servlet。

3.4.6.4 注意事项

(1)Filter常用于登录、XSS攻击、权限方面。

(2)Filter是单例的,与Servlet类似。

3.5 Servlet生命周期

3.5.1 Servlet接口方法

public class TestServletService implements Servlet{

    @Override

    public void destroy() {

        // TODO 自动生成的方法存根

    }

    @Override

    public ServletConfig getServletConfig() {

        // TODO 自动生成的方法存根

        return null;

    }

    @Override

    public String getServletInfo() {

        // TODO 自动生成的方法存根

        return null;

    }

    @Override

    public void init(ServletConfig arg0) throws ServletException {

        // TODO 自动生成的方法存根

    }

    @Override

    public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {

        // TODO 自动生成的方法存根

    }

}

其中,与生命周期相关的有四个方法:

(1)构造方法:创建servlet对象的时候调用。默认情况下,第一次访问(拥有load-on-startup设置的除外)servlet的时候创建servlet对象只调用1次。证明servlet对象在tomcat是单实例的

(2)init方法: 创建完servlet对象的时候调用。只调用1次。

(3)service方法: 每次发出请求时调用。调用n次。

(4)destroy方法: 销毁servlet对象的时候调用。停止服务器或者重新部署web应用时销毁servlet对象。只调用1次。

3.5.2 Tomcat内部代码运行

(1)通过配置文件的映射关系,找到ServletClass内容。

(2)通过反射构造Servlet对象

(3)创建ServletConfig对象,反射调用init方法。

(4)创建request、response对象,反射调用service方法。

(5)销毁servlet时,反射调用destroy方法。

3.5.3 时序图

图3-2 Servlet生命周期时序图

3.6 Servlet多线程问题

(1)servlet对象在tomcat服务器是单实例多线程的,为每个用户请求分配一个线程,可以通过线程池来管理。

(2)因为servlet是多线程的,所以当多个servlet的线程同时访问了servlet的共享数据,如成员变量,可能会引发线程安全问题。

        解决办法:

             · 把使用到共享数据的代码块进行同步(使用synchronized关键字进行同步)

             · 建议在servlet类中尽量不要使用成员变量。如果确实要使用成员,必须同步。而且尽量缩小同步代码块的范围。(哪里使用到了成员变量,就同步哪里!!),以避免因为同步而导致并发效率降低。

3.7 域对象

(1)作用: 用于保存数据、获取数据,可以在不同的动态资源之间共享数据。

(2)使用:

        · 保存数据 setAttribute(String,Object)

        · 获取数据 Object getAttribute(String)

        · 删除数据 removeAttribute(String)

(3)分类:

        · HttpServletRequest

        · HttpSession 会话对象

        · PageContext

        · ServletContext 作用范围为整个Web应用

(4)可以通过getContextPath()方法获取上下文路径

3.8 转发与重定向

(1)转发:

        · 地址栏不会改变

        · 只能转发到当前web应用

        · 可以把数据保存到request域

        · request.getRequestDispatcher(String).forward(request,response)

(2)重定向:

        · 地址栏改变

        · 可以跳转到其他web应用

        · 不能使用request数据(涉及到2次浏览器请求)

        · response.sendRedirect(String)

3.9 Cookie

    会话数据保存在客户端。浏览器在每次访问服务端时,都会带着cookie信息。

关于Cookie的跨域问题:cookie 跨域问题_chou_out_man的博客-CSDN博客_cookie跨域

3.9.1 使用

(1)构造Cookie对象

        Cookie(java.lang.String name, java.lang.String value)

(2)设置cookie

        void setPath(java.lang.String uri)  :设置cookie的有效访问路径

        void setMaxAge(int expiry):设置cookie的有效时间
                · 不设置,则随浏览器关闭而消失
                · 整数(正负均可),cookie保存到浏览器,缓存在硬盘中
                · 零,不保存cookie

        void setValue(java.lang.String newValue) :设置cookie的值

(3)发送cookie到浏览器端保存

        void response.addCookie(Cookie cookie)  :发送cookie

(4)服务器接收cookie

        Cookie[] request.getCookies() :接收cookie

3.9.2 Demo

@WebServlet("/LastAccessTime")

public class LastAccessTime extends HttpServlet {

    @Override

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        resp.setContentType("text/html;charset=utf-8");// 防止浏览器显示乱码

        String lastAccessTime = null;

        Cookie[] cookies = req.getCookies();

        for (Cookie cookie : cookies) {

            String name = cookie.getName();

            if (name.equals("lastAccessTime")) {

                astAccessTime = cookie.getValue();

                break;

            }

        }

        if (StringUtils.isEmpty(lastAccessTime)) {

                resp.getWriter().print("您是首次访问!");

        } else {

                resp.getWriter().print("你上次访问时间:" + lastAccessTime);

        }

    // 保存访问时间

    // 创建cookie 将当前时间作为cookie保存到浏览器

    String currenttime = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss").format(new Date());

    Cookie cookie = new Cookie("lastAccessTime", currenttime);

    cookie.setMaxAge(60 * 60 * 24);

    // 发送cookie

    resp.addCookie(cookie);

    }

}

3.9.3 工作原理

    真正构建Cookie是在org.apache.catalina.connector.Response类中,调用generateCookieString方法将Cookie对象构造成一个String字符串,并将字符串命名为Set-Cookie添加到Header中。

3.9.4 利弊分析

    弊端

        · 只能存字符串类型,不能保存对象

        · 只能存非中文

        · 1个Cookie的容量不超过4KB,最多300个

        · 安全性差

3.9.5 压缩cookie

(1)采用文本压缩方式。可采用gzip或者deflate算法

(2)压缩后进行转码,采用Base32或者Base64。因为Cookie中不能包含控制字符,只能包含ASCII码中34~126的可见字符。

3.10 Session

    会话数据保存在服务器端(内存中)

3.10.1 使用

HttpSession类:用于保存会话数据

(1)创建或得到session对象

        HttpSession getSession() 

        HttpSession getSession(boolean create) 

(2)设置session对象

        void setMaxInactiveInterval(int interval)  : 设置session的有效时间

        void invalidate()    : 销毁session对象

        java.lang.String getId()  : 得到session编号

(3)保存会话数据到session对象

        void setAttribute(java.lang.String name, java.lang.Object value)  : 保存数据

        java.lang.Object getAttribute(java.lang.String name)  : 获取数据

        void removeAttribute(java.lang.String name) : 清除数据

3.10.2 demo

@WebServlet("/TestSession")

public class TestSession extends HttpServlet {

    private static final long serialVersionUID = 1L;

    /**

    * @see HttpServlet#HttpServlet()

    */

    public TestSession() {

        super();

        // TODO Auto-generated constructor stub

    }

/**

* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)

*/

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // TODO Auto-generated method stub

        response.setContentType("text/html;charset=utf-8");// 防止浏览器显示乱码

        HttpSession session = request.getSession();

        String lastTime = (String) session.getAttribute("lastTime");

        if(lastTime == null) {

            response.getWriter().write("这是第一次访问");

        }else {

            response.getWriter().write("上次访问时间:" + lastTime + "现在: " + new Date());

        }

        session.setMaxInactiveInterval(60 * 60 * 24);

        session.setAttribute("lastTime", new Date()+ "");

    }

}

3.10.3 与Cookie关系

(1)服务器生成唯一对应session对象的JSESSIONID,并作为cookie发送到浏览器端保存。

(2)如果浏览器禁用cookie可以将JSESSIONID写到用户请求的URL中。

(3)如何避免浏览器的JSESSIONID的cookie随着浏览器关闭而丢失的问题

/**

* 手动发送一个硬盘保存的cookie给浏览器

*/

Cookie c = new Cookie("JSESSIONID",session.getId());

c.setMaxAge(60*60);

response.addCookie(c);

3.10.4 工作原理

(1)request.getSession()方法,触发创建session对象,并加入到org.apache.catalina.Manager的session容器中保存。

(2)Manager负责servlet容器中所有session的生命周期管理。当servlet容器重启或者关闭时,Manager负责持久化(调用upload方法)没有过期的session对象到“SESSIONS.ser”文件中,也会定期检测过期。

3.10.5 分布式session

图3-3 分布式session

(1)服务订阅服务器,集中管理资源和配置,统一通过它来推送配置。可以使用Zookeeper实现。

(2)分布式缓存,存储共享集群中每台集群的session。

(3)存取方式。
        · 自定义InnerHttpSession类重新实现HttpSession接口。
        · 通过Filter拦截用户请求,将自己设置的InnerHttpSession对象设置到request和response对象中。
        · 应用创建的所有Session对象都保存在InnerHttpSession对象中,访问完成后将InnerHttpSession内容更新到分布式缓存中。

3.10.6 跨域名共享Cookie

(1)利用跳转应用支持多个域名的访问,并将同一个sessionID作为cookie写到多个域名下。

(2)多个域名,根据sessionID在分布缓存中拿取session信息。

3.10.7 表单重复提交问题

(1)原因

        · 网络延时

        · 浏览器重新刷新按钮

        · 浏览器“回退”按钮,再提交。

(2)解决方案

        · 利用javaScript代码进行标识

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

Form表单

    用户名:

        ·利用js让按钮在提交一次后不可用

function dosubmit(){

    //获取表单提交按钮

    var btnSubmit = document.getElementById("submit");

    //将表单提交按钮设置为不可用,这样就可以避免用户再次点击提交按钮

    btnSubmit.disabled= "disabled";

    //返回true让表单可以正常提交

    return true;

}

        · 在session域中生成并保存唯一token,在页面中加入隐藏域存储token,提交时进行检验

//用户访问服务器

@WebServlet("/ForwardServlet")

public class ForwardServlet extends HttpServlet {

@Override

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        req.getSession().setAttribute("sesionToken", TokenUtils.getToken());

        req.getRequestDispatcher("form.jsp").forward(req, resp);

    }

}

//跳转页面

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

Form表单

    

        

             用户名:

                name="userName">

        

    

//服务器后端处理

HttpSession session = request.getSession();

String sesionToken = (String) session.getAttribute("sesionToken");

String token = request.getParameter("token");

if (!(token.equals(sesionToken))) {

return false;

}

session.removeAttribute("sesionToken");

3.11 Token

    随机性令牌,唯一不重复字符串。

    生成方式:

            · 自定义唯一识别码

            · UUID.randomUUID().toString()

3.12 Web安全与攻防

(1)XSS 跨站脚本注入

        利用Filter进行拦截,例如将

你可能感兴趣的:(JavaWeb)