首页 > 其他分享 >Cookie 和 Session

Cookie 和 Session

时间:2023-03-05 18:14:11浏览次数:37  
标签:resp req Session cookie Cookie 客户端

会话跟踪

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

Cookie

简介

由于HTTP协议是一个无状态的协议,服务器无法知道是哪个客户端(浏览器)访问了它,因此需要一个表示用于让服务器区分不同的客户端。Cookie就是管理服务器和客户端之间状态的标识,用于弥补HTTP协议无状态的不足

  • Cookie功能需要浏览器支持,如果浏览器不支持Cookie或者禁用了Cookie,则Cookie功能失效
  • 不同的浏览器采用不同的方式保存Cookie
  • Cookie有时也用其复数形式Cookies。类型为“小型文本文件”,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息

原理

Cookie原理即客户端第一次先服务器发送请求时,服务器在response头部设置Set-Cookie字段,客户端受到响应手就会设置Cookie并存储,在下一次该客户端向服务器发送请求时,就会在request头部自动带上Cookie字段,服务器收到Cookie用于区分不同客户端。为了Cookie与某个用户有对应关系,应该在第一次访问时就存在与服务器,此时需要Session

案例

创建Cookie

@WebServlet("/login")
public class LoginServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        /*
         * 创建Cookie
         *  1. name:Cookie的名称。Cookie一旦创建,名称便不可更改
         *  2. value:Cookie的值。如果值为Unicode字符(中文),需要为字符编码。如果值为二进制数据,则需要使用BASE64编码
         * Cookie cookie = new Cookie("wen", URLEncoder.encode("文", StandardCharsets.UTF_8.name()));
         */
        Cookie cookie = new Cookie("account", req.getParameter("account"));
        /*
         * Cookie失效的时间,单位秒,默认为–1
         * 如果为正数,则该Cookie在maxAge秒之后失效
         * 如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie
         * 如果为0,表示删除该Cookie
         */
        cookie.setMaxAge(60 * 60);

        //Cookie是否仅被使用安全协议传输。安全协议有HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false
        cookie.setSecure(true);

        //Cookie是不可跨域名的,正常情况下,同一个一级域名下的两个二级域名也不能交互使用Cookie,如 map.baidu.com 和 www.baidu.com
        //如果想所有baidu.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数
        cookie.setDomain(".baidu.com");

        //决定允许访问Cookie的路径(ContextPath),如只允许/session/下的程序使用Cookie,但/session/test/a.jsp获取不到/session/source/b.jsp的Cookie
        //设置为"/"时允许所有路径使用Cookie。path属性需要使用符号"/"结尾。name相同但domain相同的两个Cookie也是两个不同的Cookie
        cookie.setPath("/session/");

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

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       doPost(req,resp);
    }
}

请一次请求无对应的Cookie,请求完后返回Set-Cookie

第二次请求带上Cookie

获取Cookie

@WebServlet("/login2")
public class LoginServlet2 extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //通过request对象获取Cookies数组
        Cookie[] cookies = req.getCookies();
        for (Cookie cookie : cookies) {
            System.out.println("name: "+cookie.getName()+",value: "+ URLDecoder.decode(cookie.getValue(), StandardCharsets.UTF_8.name()));
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       doPost(req,resp);
    }
}

输出

缺陷

  • 数量和大小受限:客户端能创建Cookie数量最多300个,并且每个大小不能超过4kb;每个Web站点能设置Cookie总数不超过20个
  • 安全问题:存在跨站点脚本攻击的可能,在受到跨站点脚本攻击时,脚本指令将会读取当前站点的所有Cookie 内容(已不存在 Cookie 作用域限制),然后通过某种方式将 Cookie 内容提交到指定的服务器(如:AJAX)。一旦 Cookie 落入攻击者手中,它将会重现其价值
  • 客户端禁用Cookie:禁用Cookie后,则Cookie功能失效

应用场景

主要的应用场景是用作客户端和服务器之间的会话状态保持


Session

简介

Session(会话)是浏览器第一次访问服务端,服务端就会创建一次会话,在会话中存储特定用户会话所需的属性及配置信息。它与Cookie的区别就是Session是缓存在服务端的,Cookie则是缓存在客户端,它们都由服务端生成,为了弥补Http协议无状态的缺陷

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

原理

Session原理是,当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。服务器会向客户浏览器发送一个每个用户特有的会话编号sessionID,放到Cookie里。服务器同时也把sessionID和对应的用户信息、用户操作记录在服务器上,这些记录就是Session。客户端再次访问时会带入到Cookie发送给服务器,其中就包含sessionID。服务器从Cookie里找到sessionID,再根据sessionID找到以前记录的用户信息就可以知道客户端之前操控、访问过哪里

 

案例

创建session

@WebServlet("/login")
public class LoginServlet extends HttpServlet {

    private static final Map<String,String> DATA = new HashMap<String,String>(){
        {
            put("yi","yi");
            put("wen","wen");
        }
    };

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        resp.setHeader("Content-type", "text/html;charset=UTF-8");
        String account = req.getParameter("account");
        if (DATA.containsKey(account)) {
            //获取Session对象
            HttpSession session = req.getSession();
            //设置Session中的属性
            session.setAttribute("account",account);
            
            resp.getWriter().write("用户:"+ account +"登录成功");
        } else {
            resp.getWriter().write("登录失败");
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       doPost(req,resp);
    }
}

第一次访问服务器,服务器创建一个session的JSESSIONID返回

第二次访问服务器,客户端携带JSESSIONID访问

获取session

@WebServlet("/login2")
public class LoginServlet2 extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        Cookie[] cookies = req.getCookies();
        for (Cookie cookie : cookies) {
            System.out.println("name: "+cookie.getName()+",value: "+ URLDecoder.decode(cookie.getValue(), StandardCharsets.UTF_8.name()));
        }

        //获取Session对象
        HttpSession session = req.getSession();
        //获取Session中的属性
        System.out.println(session.getAttribute("account"));
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       doPost(req,resp);
    }
}

输出

缺陷

 Session存放到服务器内存中,如果大量请求,那么服务器存放Session内存增大,影响服务器性能

应用场景

主要的应用场景是用作客户端和服务器之间的会话状态保持


 Session-Cookie认证

实现思路

  1. 服务器在接受客户端首次访问时在服务器端创建seesion,然后保存seesion(可以将seesion保存在内存中,也可以保存在redis中,推荐使用后者),然后给这个session生成一个唯一的标识字符串,然后在响应头中放下这个唯一标识字符串

  2. 签名。这一步通过秘钥对sid进行签名处理,避免客户端修改sid(非必需步骤)

  3. 浏览器中收到请求响应的时候会解析响应头,然后将sid保存在本地cookie中,浏览器在下次HTTP请求的请求头中会带上该域名下的cookie信息

  4. 服务器在接受客户端请求时会去解析请求头cookie中的sid,然后根据这个sid去找服务器端保存的该客户端的session,然后判断该请求是否合法

优缺点

Session-Cookie 认证机制在基本上所有的网页浏览器上都能够支持,而且实现方式也很简单

  1. 当存在多台服务器时会出现session同步问题
  2. 很容易遭受到Cookie欺骗和CRFS攻击
  3. 服务端存储压力,当很多的session存储到服务端时,会对服务器的存储造成压力
  4. 跨域问题,Cookie是属于同源策略限制的条件之一

 登录Servlet

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {

    private static final Map<String,String> DATA = new HashMap<String,String>(){
        {
            put("yi","yi");
            put("wen","wen");
        }
    };

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        resp.setHeader("Content-type", "text/html;charset=UTF-8");
        String account = req.getParameter("account");
        String password = req.getParameter("password");
        if (DATA.containsKey(account) && DATA.get(account).equals(password)) {
            HttpSession session = req.getSession();
            session.setAttribute("sid",account);
            Cookie cookie = new Cookie("sid", account);
            resp.addCookie(cookie);
            //resp.getWriter().write("登录成功");
            resp.sendRedirect("/index.jsp");
        } else {
            resp.getWriter().write("登录失败");
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       doPost(req,resp);
    }
}

 退出登录Servlet

@WebServlet("/loginOutServlet")
public class LoginOutServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        resp.setHeader("Content-type", "text/html;charset=UTF-8");
        //将session失效
        req.getSession().invalidate();
        Cookie cookie = new Cookie("account", "");
        //将客户端cookie清除
        cookie.setMaxAge(0);
        resp.addCookie(cookie);

        resp.getWriter().write("退出成功");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       doPost(req,resp);
    }
}

 全局拦截器,校验是否登录

@WebFilter("/*")
public class LoginFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        //资源过滤
        Set<String> urls = new HashSet<String>() {
            {
                add("/login.jsp");
                add("/imgs/");
                add("/css/");
                add("/register.jsp");
                add("/loginServlet");
                add("/registerServlet");
                add("/loginOutServlet");
            }
        };
        String url = req.getRequestURL().toString();
        for (String s : urls) {
            if (url.contains(s)) {
                chain.doFilter(request,response);
                return;
            }
        }

        HttpSession session = req.getSession();
        String sid = (String) session.getAttribute("sid");
        if (Objects.nonNull(sid)) {
            chain.doFilter(request,response);
        } else {
            /*
            req.setAttribute("login-tip","未登录");
            req.getRequestDispatcher("/login.jsp").forward(req,response);
            */
            resp.setHeader("Content-type", "text/html;charset=UTF-8");
            resp.getWriter().write("未登录");
        }
    }

    @Override
    public void destroy() {

    }
}

 

标签:resp,req,Session,cookie,Cookie,客户端
From: https://www.cnblogs.com/52-IT-y/p/17180783.html

相关文章