zoukankan      html  css  js  c++  java
  • Session与Cookie(自定义Session)

    一、会话管理

      会话管理: 管理浏览器客户端 和 服务器端之间会话过程中产生的会话数据。

      域对象: 实现资源之间的数据共享。

      request域对象

      context域对象

      会话技术:  

        Cookie技术:会话数据保存在浏览器客户端。

        Session技术:会话数据保存在服务器端。存放在内存里面,客户端与服务器端之前的通讯使用SessionId

    二、Cookie技术

    2.1 特点:Cookie技术:会话数据保存在浏览器客户端。

    2.2 Cookie技术核心

      Cookie类:用于存储会话数据

        1)构造Cookie对象

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

        2)设置cookie

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

          void setMaxAge(int expiry) : 设置cookie的有效时间

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

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

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

        4)服务器接收cookie

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

    2.3 Cookie原理

      1)服务器创建cookie对象,把会话数据存储到cookie对象中。

        new Cookie("name","value");

      2)  服务器发送cookie信息到浏览器

        response.addCookie(cookie);

         举例: set-cookie: name=eric  (隐藏发送了一个set-cookie名称的响应头)

      3)浏览器得到服务器发送的cookie,然后保存在浏览器端。

      4)浏览器在下次访问服务器时,会带着cookie信息

        举例: cookie: name=eric  (隐藏带着一个叫cookie名称的请求头)

      5)服务器接收到浏览器带来的cookie信息

        request.getCookies();

    2.4 Coolie的细节  

      1)void setPath(java.lang.String uri)   :设置cookie的有效访问路径。有效路径指的是cookie的有效路径保存在哪里,那么浏览器在有效路径下访问服务器时就会带着cookie信息,否则不带cookie信息。

      2)void setMaxAge(int expiry) : 设置cookie的有效时间。

        正整数:表示cookie数据保存浏览器的缓存目录(硬盘中),数值表示保存的时间。

        负整数:表示cookie数据保存浏览器的内存中。浏览器关闭cookie就丢失了!!

        零:表示删除同名的cookie数据

      3)Cookie数据类型只能保存非中文字符串类型的。可以保存多个cookie,但是浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。

    2.5 Cookie 问题

      1) cookie不能跨浏览器

      2)浏览器关闭,默认失效cookie

      3)127.0.0.1和localhost,操作cookie时不共享

    2.6 Cookie的局限:

      1)Cookie只能存字符串类型,不能保存对象

      2)只能存非中文

      3)1个cookie的容量不超过4KB

    2.7 案例-显示用户上次访问时间

    @WebServlet("/LastAccessTime")
    public class LastAccessTime extends HttpServlet {
        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")) {
                    lastAccessTime = 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);
        }
        String addCookie(String key, String value, HttpServletResponse resp) {
            return key;
        }
    }

    三、Session技术

    3.1 由于Cookie的局限性和问题,想要突破这些限制只能使用session技术!

      Session特点:会话数据保存在服务器端。(内存中)

    3.2 Session 技术核心

      HTTPSession类:用于保存会话数据

      1)创建或得到session对象

        HttpSession getSession() 

        HttpSession getSession(boolean create)   //true,没有session,会创建。False则不会创建

      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.3 Session原理 

      服务器端创建session,通过响应头将sessionId保存到客户端本地。客户端通过请求头,将sessionId传递到服务器端。

      关闭浏览器只会清楚客户端的sessionID,服务器端的session由创建时的时间失效决定。

    代码解读:HttpSession session = request.getSession();

      1)第一次访问创建session对象,给session对象分配一个唯一的ID,叫JSESSIONID

         new HttpSession();

      2)把JSESSIONID作为Cookie的值发送给浏览器保存

        Cookie cookie = new Cookie("JSESSIONID", sessionID);

        response.addCookie(cookie);

      3)第二次访问的时候,浏览器带着JSESSIONID的cookie访问服务器

      4)服务器得到JSESSIONID,在服务器的内存中搜索是否存放对应编号的session对象。

        if(找到){

          return map.get(sessionID);

        }else{

          new HttpSession();

        }

      5)如果找到对应编号的session对象,直接返回该对象

      6)如果找不到对应编号的session对象,创建新的session对象,继续走1的流程

      结论:通过JSESSION的cookie值在服务器找session对象!

    3.4 Session细节

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

      2)两个getSession方法:

        getSession(true) / getSession()  : 创建或得到session对象。没有匹配的session编号,自动创建新的session对象。

        getSession(false):              得到session对象。没有匹配的session编号,返回null

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

        session对象销毁时间:

        3.1 默认情况30分服务器自动回收

        3.2 修改session回收时间

        3.3 全局修改session有效时间

    <!-- 修改session全局有效时间:分钟 -->
        <session-config>
            <session-timeout>1</session-timeout>
        </session-config>

        3.4.手动销毁session对象  

          void invalidate()     : 销毁session对象

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

      /**
      * 手动发送一个硬盘保存的cookie给浏览器
      */   Cookie c = new Cookie("JSESSIONID",session.getId());   c.setMaxAge(60*60);   response.addCookie(c);

    总结:

      1)会话管理: 浏览器和服务器会话过程中的产生的会话数据的管理。

      2)Cookie技术:

        new Cookie("name","value")

        response.addCookie(coookie)

        request.getCookies()

      3)Session技术

        request.getSession();                                            

        setAttrbute("name","会话数据");

        getAttribute("会话数据")

    四、自定义缓存

    4.1定义缓存实体类

    public class Cache {
    public Cache(String key, Object value, Long timeOut) {
            super();
            this.key = key;
            this.value = value;
            this.timeOut = timeOut;
        }
    public Cache() {
    }
        /**
         * key
         */
        private String key;
        /**
         * 缓存数据
         */
        private Object value;
        /**
         * 超时时间
         */
        private Long timeOut;
    }

    4.2 定义缓存类

    /**
     * @classDesc: 功能描述:(缓存map)
     */
    public class CacheManager {
        private Map<String, Cache> cacheMap = new HashMap<>();
        /**
         * @methodDesc: 功能描述:(往缓存存值)
         */
        public void put(String key, Object oj) {
            put(key, oj, null);
        }
        /**
         * @methodDesc: 功能描述:(往缓存存值)
         */
        public synchronized void put(String key, Object oj, Long timeOut) {
            if (oj == null) {
                return;
            }
            Cache cache = new Cache();
            cache.setKey(key);
            if (timeOut != null)
                cache.setTimeOut(timeOut + System.currentTimeMillis());
            cache.setValue(oj);
            cacheMap.put(key, cache);
        }
        /**
         * @methodDesc: 功能描述:(删除)
         */
        public synchronized void deleteCache(String key) {
            cacheMap.remove(key);
        }
        /**
         * @methodDesc: 功能描述:(获取缓存中数据)
         */
        public synchronized Object get(String key) {
            Cache cache = cacheMap.get(key);
            Object oj = null;
            if (cache != null) {
                oj = cache.getValue();
            }
            return oj;
        }
        /**
         * @methodDesc: 功能描述:(检查数据是否在有效期内)
         */
        public synchronized void checkValidityData() {
            for (String key : cacheMap.keySet()) {
                Cache cache = cacheMap.get(key);
                Long timeOut = cache.getTimeOut();
                if (timeOut == null) {
                    return;
                }
                long currentTime = System.currentTimeMillis();
                long endTime = timeOut;
                long result = (currentTime - endTime);
                if (result > 0) {
                    System.out.println("清除:"+key);
                    cacheMap.remove(key);
                }
            }
        }
        public static void main(String[] args) throws InterruptedException {
            CacheManager cacheManager = new CacheManager();
            // cacheManager.put("lisi", 0);
            cacheManager.put("zhangsan", "jj", 5000l);
            // 多线程,验证session的有效时间
            ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
            scheduledThreadPool.schedule(new Runnable() {
                public void run() {
                    cacheManager.checkValidityData();
                }
            }, 5000, TimeUnit.MILLISECONDS);
            Thread.sleep(5000);
            System.out.println(cacheManager.get("zhangsan"));
        }
    }

    五、自定义Token

    5.1 什么是token

      token其实就是一个令牌,具有随机性,类似于sessionId。在对接一些第三方平台的时候,为了能够保证数据安全性,通常会使用一些令牌进行交互。

    5.2 如何自定义token

      token生成规则,只要保证token生成一个不重复的唯一字符串即可。使用jdk自带的uuid生成规则。

    5.3 什么是UUID

      UUID含义是通用唯一识别码 (Universally Unique Identifier),这是一个软件建构的标准,也是被开源软件基金会 (Open Software Foundation, OSF) 的组织应用在分布式计算环境 (Distributed Computing Environment, DCE) 领域的一部分。

      UUID 的目的,是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。如此一来,每个人都可以建立不与其它人冲突的 UUID。

    5.4 UUID的组成

      UUID保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成的API。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字
      UUID由以下几部分的组合:
        (1)当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。
        (2)时钟序列。
        (3)全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。
      UUID的唯一缺陷在于生成的结果串会比较长。关于UUID这个标准使用最普遍的是微软的GUID(Globals Unique Identifiers)。在ColdFusion中可以用CreateUUID()函数很简单地生成UUID,其格式为:xxxxxxxx-xxxx- xxxx-xxxxxxxxxxxxxxxx(8-4-4-16),其中每个 x 是 0-9 或 a-f 范围内的一个十六进制的数字。而标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)。

    5.5 UUID代码

      UUID。randomUUID().toString()

    六、表单重复提交解决方案(防止http重复提交) 

    6.1 场景描述:

      网络延时:在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交。

      重复刷新:表单提交后用户点击【刷新】按钮导致表单重复提交。

      后退再提交:点击浏览器的【后退】按钮回退到表单页面后进行再次提交

    6.2 解决方案

      6.2.1 使用javascript 解决

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <title>Form表单</title>
    <script type="text/javascript">
        var isFlag = false; //表单是否已经提交标识,默认为false
        function submitFlag() {
            if (isFlag == false) {
                isFlag = true;
                return true;
            } else {
                return false;
            }
        }
    </script>
    </head>
    <body>
        <form action="${pageContext.request.contextPath}/DoFormServlet"
            method="post" onsubmit="return submitFlag()">
            用户名:<input type="text" name="userName"> <input type="submit"
                value="提交" id="submit">
        </form>
    </body>
    </html>

      6.2.2 使用后端提交解决

      对于【重复刷新】、【后退再提交】导致表单重复提交的问题,前端无法解决,只能在服务器端解决。

      在服务器端生成一个唯一的随机标识号,专业术语称为Token(令牌),同时在当前用户的Session域中保存这个Token。然后将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。

      在下列情况下,服务器端将拒绝处理用户提交的表单请求:

    1. 存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。
    2. 当前用户的Session中不存在Token(令牌)
    3. 用户提交的表单数据中没有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"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <title>Form表单</title>
    
    </head>
    
    <body>
        <form action="${pageContext.request.contextPath}/DoFormServlet"
            method="post" onsubmit="return dosubmit()">
            <input type="hidden" name="token" value="${sesionToken}"> 用户名:<input type="text"
                name="userName"> <input type="submit" value="提交" id="submit">
        </form>
    </body>
    </html>

      后端代码:

    @WebServlet("/DoFormServlet")
    public class DoFormServlet extends HttpServlet {
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            req.setCharacterEncoding("UTF-8");
            boolean flag = isFlag(req);
            if (!flag) {
                resp.getWriter().write("已经提交...");
                System.out.println("数据已经提交了..");
                return;
            }
            String userName = req.getParameter("userName");
            try {
                Thread.sleep(300);
            } catch (Exception e) {
            }
            System.out.println("往数据库插入数据...." + userName);
            resp.getWriter().write("success");
        }
    
        public boolean isFlag(HttpServletRequest request) {
            HttpSession session = request.getSession();
            String sesionToken = (String) session.getAttribute("sesionToken");
            String token = request.getParameter("token");
            if (!(token.equals(sesionToken))) {
                return false;
            }
            session.removeAttribute("sesionToken");
            return true;
        }
    }

    七、Servlet之Filter

    7.1 什么是Filter

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

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

    7.2 Fileter使用

    import java.io.IOException;
    import java.util.Map;
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    /**
     *  使用Filter 打印参数
     *
     */
    public class FilterDemo implements Filter {
        public  FilterDemo(){
            System.out.println("FilterDemo 构造函数被执行...");
        }
        /**
         * 销毁
         */
        public void destroy() {
          System.out.println("destroy");
        }
        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<String, String[]> parameterMap = request.getParameterMap();
            for (String key : parameterMap.keySet()) {
                 String[] arr=parameterMap.get(key);
            }
        }
        /**
         * 初始化
         */
        public void init(FilterConfig paramFilterConfig) throws ServletException {
             System.out.println("init");
        }
    }

      web.xml配置

    <filter>
          <filter-name>FilterDemo</filter-name>
          <filter-class>com.stu.servlet.FilterDemo</filter-class>
        </filter>
        <filter-mapping>
        <filter-name>FilterDemo</filter-name>
        <url-pattern>/*</url-pattern>
        </filter-mapping>

    7.3 使用Filter防止XSS攻击

      7.3.1 什么是XSS攻击

        XSS攻击使用Javascript脚本注入进行攻击,例如在表单中注入: <script>location.href='http://www.baidu.com'</script>

      fromToXss.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
        <form action="XssDemo" method="post">
            <input type="text" name="userName"> <input type="submit">
        </form>
    </body>
    </html>
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebServlet("/XssDemo")
    public class XssDemo extends HttpServlet {
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String userName = req.getParameter("userName");
            req.setAttribute("userName", userName);
            req.getRequestDispatcher("showUserName.jsp").forward(req, resp);
        }
    }

      show.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>userName:${userName}
    </body>
    </html>

      73..2 解决方案:使用Filter过滤器过滤注入标签

    import java.io.IOException;
    import java.util.Map;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class FilterDemo implements Filter {
        public FilterDemo() {
            System.out.println("FilterDemo 构造函数被执行...");
        }
        /**
         * 销毁
         */
        public void destroy() {
            System.out.println("destroy");
        }
        public void doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse,
                FilterChain paramFilterChain) throws IOException, ServletException {
            System.out.println("doFilter");
            HttpServletRequest request = (HttpServletRequest) paramServletRequest;
            XssAndSqlHttpServletRequestWrapper xssRequestWrapper = new XssAndSqlHttpServletRequestWrapper(request);
            // HttpServletResponse response = (HttpServletResponse)
            // paramServletResponse;
            // // 请求地址
            // String requestURI = request.getRequestURI();
            // System.out.println("requestURI:" + requestURI);
            // // 参数
            // Map<String, String[]> parameterMap = request.getParameterMap();
            // for (String key : parameterMap.keySet()) {
            // String[] arr = parameterMap.get(key);
            // System.out.print("key:");
            // for (String string : arr) {
            // System.out.println(string);
            // }
            // }
            paramFilterChain.doFilter(xssRequestWrapper, paramServletResponse);
        }
        /**
         * 初始化
         */
        public void init(FilterConfig paramFilterConfig) throws ServletException {
            System.out.println("init");
        }
    }

      XssAndSqlHttpServletRequestWrapper

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import org.apache.commons.lang3.StringEscapeUtils;
    import org.apache.commons.lang3.StringUtils;
    
    /**
     * 防止XSS攻击
     */
    public class XssAndSqlHttpServletRequestWrapper extends HttpServletRequestWrapper {
        HttpServletRequest request;
        public XssAndSqlHttpServletRequestWrapper(HttpServletRequest request) {
            super(request);
            this.request = request;
        }
        public String getParameter(String name) {
            String value = request.getParameter(name);
            System.out.println("name:" + name + "," + value);
            if (!StringUtils.isEmpty(value)) {
                // 转换Html
                value = StringEscapeUtils.escapeHtml4(value);
            }
            return value;
        }
    }
  • 相关阅读:
    TreeSelect组件:vmodel语法糖进行父子组件传值案例
    带 icon 的输入框:slot方式。slot="prefix"和slot="suffix"
    双向绑定vmodel与单向绑定vbind:value
    使用Set集合对List集合去重
    前端报错: error in ./src/assets/fonts/iconfont.svg?t=1523541245904,Module parse failed: Unexpected token (1:0),vue.config.js中引入chainWebpack后报错
    当主键不是id时,而是其他字段,那么该字段要加上@Id注解(除了表中指定主键,实体类中也要用@Id指定主键)
    java将文件转为base64字符串和将base64字符串转为文件
    父组件向子组件传递getList方法:provide/inject
    带卡片的input输入框
    C#中使用#if DEBUG
  • 原文地址:https://www.cnblogs.com/woniusky/p/10696647.html
Copyright © 2011-2022 走看看