Cookie 和 Session

本文详细解释了Cookie的工作原理、如何在JavaServlet中操作Cookie,以及Session的机制,包括Session的创建、存储、关联与登录过程。重点讨论了Cookie与Session在用户身份验证中的联动作用。
摘要由CSDN通过智能技术生成

1. 回顾

Cookie是浏览器在本地持久化存储结构的一种机制.

1.1 Cookie的数据从哪里来?

服务器返回给浏览器的.

1.2 Cookie的数据是什么样的?

Cookie的数据是键值对结构.

并且这里的键值对都是程序员自定义的.

1.3 Cookie的作用是什么?

Cookie可以在浏览器这边存储一些"临时性的数据".

其实最典型的一种使用方式,就是用来存储"身份标识".(sessionId)

这里涉及到 Cookie和Session之间的联动.

后续在访问该网站的其它页面的时候,请求中就会自动带上刚才的sessionId,进一步服务器就可以知道当前是哪个用户在操作了.

1.4 Cookie到哪里去?

Cookie的内容会在下次访问该网站的时候,自动的被带到HTTP请求中.

1.5 Cookie怎么存的?

浏览器按照不同的"域名",分别存储Cookie.

域名和域名之间的Cookie是不能干扰的.

Cookie存储在硬盘上,往往会有一个超时时间. 

2. Cookie操作

2.1 方法 

①HttpServletRequest 类中的相关方法:

方法描述
Cookie[] getCookies()返回一个数组, 包含客户端发送该请求的所有的 Cookie 对象. 会自动把 Cookie 中的格式解析成键值对.

②HttpServletResponse 类中的相关方法:

方法描述
void addCookie(Cookie cookie)把指定的 cookie 添加到响应中.

把Cookie添加到响应中,在HTTP响应报文里,加了一个Set-Cookie header.


③Cookie 类中的相关方法:

每个 Cookie 对象就是一个键值对.

方法描述
String getName()该方法返回 cookie 的名称。名称在创建后不能改变。(这个值是 Set Cooke 字段设置给浏览器的)
String getValue()该方法获取与 cookie 关联的值
void setValue(String newValue)该方法设置与 cookie 关联的值。

 

2.2 方法实现

@WebServlet("/setcookie")
public class SetCookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //期望通过这个doGet方法,把一个自定义的Cookie数据返回到浏览器这边

        Cookie cookie = new Cookie("date","2023-04-02");
        resp.addCookie(cookie);
        Cookie cookie2 = new Cookie("time","15:18");
        resp.addCookie(cookie2);
        resp.getWriter().write("setCookie ok");

    }
}
@WebServlet("/getcookie")
public class GetCookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取到这次请求的cookie
        Cookie[] cookies = req.getCookies();
        if (cookies != null){
            for (Cookie cookie : cookies) {
                System.out.println(cookie.getName() + ": " + cookie.getValue());
            }
        }else {
            System.out.println("请求中没有cookie");
        }

        resp.getWriter().write("ok");
    }

}

①首先,发送getcookie请求:

 

此时并没有Cookie,自然getcookie请求里面也没有.

②下面发起setcookie请求:

请求这里并没有cookie内容:

响应这边:

 

访问setcookie请求的时候,代码中就会构造Cookie放在响应中.

进一步会写入浏览器,浏览器就会持久化保存.

 

③后续再次发送请求的时候,cookie内容就会出现在请求中:

再次发送,服务器就能拿到cookie内容了.(浏览器已经有了)

这个过程中,可以看到实际上真正发挥作用,还得是在服务器这边的逻辑中生效的. 

 

3. 理解会话机制

3.1 Session相关方法 

Servlet也提供了Session相关的支持.

实现登录功能,不需要直接使用Cookie AIP,直接使用Session的API就可以了.


①HttpServletRequest 类中的相关方法:

方法描述
HttpSession getSession()在服务器中获取会话. 参数如果为 true, 则当不存在会话时新建会话; 参数如果 为 false, 则当不存在会话时返回 null

getSession方法中,最核心的API.

效果有两方面:

1.如果当前用户没有session(会话),就会创建出session.

2.如果已经有了session,就能查询到这个session.

getSession背后具体做的事情:

①先读取请求中的Cookie,看Cookie里是否有JSESSIONID属性,以及值是什么.

(平时说的SessionId是一个广义的概念,不同的库中具体实现过程中会有一些细节的差异,在Servlet中,把这个属性具体叫做JSESSIONID)

如果没有,就认为需要创建新对话,

如果有,就拿着这个Id去查询看看当前的Session是否存在.

  如果存在,就直接返回该Session

  如果不存在,就准备创建新对话.

②当前确实需要创建对话,就会创建出一个Session对象,同时生成唯一的一个JSESSIONID,以该JSESSIONID为key,Session对象为value,把这个键值对给插入到服务器上述的哈希表中.

③刚才生成的JSESSION又会通过addCookie方法,加入到响应中.

此时响应里就会带有Set-Cookie字段,这里的值就是JSESSION=xxxx.

通过响应,就把JSESSION返回到浏览器这边了.

问:

什么情况下会有SessionId但没有Session?

服务器这边存储上述这些Session对象也是在内存中进行的.

比如把SessionId返回给服务器了,SessionId和Session对象是保存在服务器内存的,此时如果服务器重启,内存中的这些会话数据就没了.

但是浏览器中的SessionId这个Cookie还是存在的.

Session保存在内存中,这样的设定其实也不是很合理.

因此实践中往往会把会话保存在其它的介质上,来达到"持久化存储"目的.


②HttpSession 类中的相关方法:

一个 HttpSession 对象里面包含多个键值对.

方法描述
Object getAttribute(String name)该方法返回在该 session 会话中具有指定名称的对象,如果没有 指定名称的对象,则返回 null.
void setAttribute(String name, Object value)该方法使用指定的名称绑定一个对象到该 session 会话
boolean isNew()判定当前是否是新创建出的会话

Session存在的意义,也是为了让用户能够保存一些自定义的数据.

此处Session更像是一个Map<String.Object>.

此时,程序员想保持什么样的键值对,就可以直接进行设置了.


3.2 Session整体的存储的数据结构

Session在一个服务器上,存在很多份,每个用户都应该有一个自己的Session.

服务器同时会有多个用户,服务器就会使用Map的方式来组织多个Session.

 

多个客户端对应多个键值对,key是sessionId,value是Session对象.

这些会话里面,每个会话又可以存一些用户的键值对(自定义的).

4. 实现用户登陆

4.1 前端

当前,页面是一个form表单.

当用户点击登陆按钮,就会发起一个HTTP请求. 

形如:

服务器这边就可以根据这个请求做出处理.

 

4.2 后端

4.2.1 登陆页面 

@WebServlet("/login")
public class LoginServlet extends HelloServlet{
    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取到用户的用户名和密码
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if (username == null || password == null || username.equals("") || password.equals("")) {
            //确定引用本身非空,内容非空
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("请求的参数不完整!");
            return;
        }

        //2.验证用户名密码是否正确.
        //正确的验证,是从数据库读取数据.
        //注册账号,就会给数据库插入用户名和密码.
        //登陆的时候,就是验证当前用户名是否存在,密码是否匹配.
        //当前为了简单,先不引入数据库,直接通过硬编码的方式来判定用户密码
        //此处约定,合法的用户名是zhangshan,密码123
        if (!username.equals("zhangshan")){
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("用户名错误!");
            return;
        }
        if (!password.equals("123")){
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("密码错误!");
            return;
        }

        //3.登陆成功!!
        //此时就可以给用户创建会话
        HttpSession session = req.getSession(true);
        //在会话中,可以顺便保存点自定义的数据,比如保存一个登陆的时间戳.
        //setAttribute后面的value是个Object,想存什么都可以.
        session.setAttribute("username",username);
        session.setAttribute("time",System.currentTimeMillis());
        
        //4.让页面自动跳转到网站主页.
        //此处约定主页的路径是index(也使用Servlet生成一个动态页面)
        resp.sendRedirect("index");
    }
}

 4.2.2 跳转页面

//通过这个Servlet生成一个主页
@WebServlet("/index")
public class IndexServlet extends HelloServlet{
    @Override
    //重定向的请求是Get请求
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //先验证一下用户的登陆状态
        //如果未登陆,就要求用户先登陆
        HttpSession session = req.getSession(false);
        if (session == null){
            //用户未登陆
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("请先登陆!!!");
            return;
        }
        //已经登陆成功.
        //取出之前的attribute
        String username = (String) session.getAttribute("username");
        Long time = (Long) session.getAttribute("time");
        System.out.println("username="+username + ", time= "+time);

        //根据这样的内容去构造页面.
        resp.setContentType("text/html;charset=utf8");
        resp.getWriter().write("欢迎您, "+ username + "! 上次登陆时间: "+time);
    }
}

 

4.2.3 总结 

此时先访问index页面,是未登陆的状态:

此时故意输出用户名:

输入对的:

这个登陆时间,取决于我们的登陆时间,而不是访问时间.


总结:

这个程序就涉及到三个部分进行联动.

1.登陆页面(静态的html,使用form表单构造HTTP请求).

2.LoginServlet(doPost处理登陆的逻辑流程).

3.IndexServlet(doGet处理主页的生成).

在会话中存储的这些attribute,就相当于"埋下伏笔",后续其它页面中就可以使用到这里的东西.

  • 10
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值