目录
1. 理解 Cookie 机制和 Session 机制
2. Cookie 和 Session 的区别
3. Servlet 对于 Cookie 和 Session 提供的 API
4. 实现用户登录
4.1 理解 req.getSession()
4.2 理解上述代码客户端和服务器交互过程
由于HTTP 协议自身是属于 "无状态" 协议. 也就是说默认情况下 HTTP 协议的客户端和服务器之间的这次通信, 和下次通信之间没有直接的联系.
但是很多时候,一些登录网站, 我们首次登录的时候, 需要输入账号密码, 第二次登录的时候, 服务器就应该能够知道当前用户此次请求是否已经登录过, 服务器如何才能知道呢? 搭配 Cookie 和 Session 就能够让服务器知道了.
1. 浏览器站在安全的角度考虑, 不敢让页面直接来访问文件系统, Cookie 就是浏览器给 HTTP 协议提供的一个持久化存储数据的方案.
2. Cookie 里面存储的是键值对, 不能存储复杂的对象, 只能存储字符串; 并且 Cookie 是按照域名来进行分类存储的.
3.Cookie 数据从哪来, 到哪去? Cookie 数据来自于服务器, 服务器代码想让浏览器存啥, 就返回啥 (通过在 HTTP 响应报文中的 Header 加入 Set-Cookie) ; Cookie 数据又回到服务器去, 每次给服务器发送的 HTTP 请求, 就会带上之前存储的 Cookie 信息 (在请求 Header 中带有 Cookie 属性).
Cookie 的典型应用: 登录网站中保存用户的身份信息
具体实现:
1. 可以让服务器把用户的所有信息通过 Set-Cookie 返回给浏览器, 直接在浏览器保存, 但是这种做法是不推荐的, 一是因为用户可以手动清除 Cookie 数据 , 其次就是敏感的信息通过网络传输不安全.
2. 更典型靠谱的做法是在服务器这边保存用户的详细信息, 使用键值对来进行保存 (Session) ,键是服务器自动生成的一串唯一字符串 (SessionId), 值就是用户的详细信息了, 服务器就可以把这个键通过 Set-Cookie 返回给浏览器了.
上述流程就类似于去医院看病, 医院就是服务器, 患者就是客户端.
1. 首次去医院的时候, 医院没有这个患者的身份信息, 就要求患者去办理就诊卡.
2. 办理就诊卡就需要提供身份证等信息, 就会在医院的系统中建档 (创建了一个session), 同时医院会给患者一个就诊卡 (包含SessionId) .
3. 后续这个患者去其他科室做检查, 只要刷就诊卡就行了.
1. Cookie 是客户端的机制, Session 是服务器的机制.
2. Cookie 和 Session 常常会在一起搭配使用, 但是不是必须要搭配.
3.完全可以用 Cookie 来保存一些数据在客户端. 这些数据不一定是用户身份信息, 也不一定是
token / sessionId ; Session 中的 token / sessionId 也不需要非得通过 Cookie / Set-Cookie 传递 .
方法
|
描述 |
HttpSession getSession()
|
在服务器中获取会话 . 参数如果为 true, 则当不存在会话时新建会话 ; 参数如果
为 false, 则当不存在会话时返回 null
|
Cookie[] getCookies()
|
返回一个数组 , 包含客户端发送该请求的所有的 Cookie 对象 . 会自动把
Cookie 中的格式解析成键值对 .
|
方法
|
描述 |
void addCookie(Cookie cookie) | 把指定的 cookie 添加到响应中. |
方法
|
描述 |
Object getAttribute(String name)
|
该方法返回在该 session 会话中具有指定名称的对象,如果没
有指定名称的对象,则返回 null.
|
void setAttribute(String name, Object value)
|
该方法使用指定的名称绑定一个对象到该 session 会话 |
boolean isNew() | 判定当前是否是新创建出的会话 |
方法
|
描述 |
Object getAttribute(String name)
|
该方法返回在该 session 会话中具有指定名称的对象,如果没有指定名称的对象,则返回 null.
|
void setAttribute(String name, Object value)
|
该方法使用指定的名称绑定一个对象到该 session 会话 |
boolean isNew() | 判定当前是否是新创建出的会话 |
方法
|
描述 |
String getName() |
该方法返回 cookie 的名称。名称在创建后不能改变。 ( 这个值是 Set-
Cooke 字段设置给浏览器的 )
|
String getValue() | 该方法获取与 cookie 关联的值 |
void setValue(String newValue)
|
该方法设置与 cookie 关联的值。 |
1. HTTP 的 Cooke 字段中存储的实际上是多组键值对 . 每个键值对在 Servlet 中都对应了一个 Cookie 对象.2. 通过 HttpServletRequest.getCookies() 获取到请求中的一系列 Cookie 键值对 .3. 通过 HttpServletResponse.addCookie() 可以向响应中添加新的 Cookie 键值对 .
此处的用户登录就简单原则实现,主要是理解 Cookie 和 Session 的机制.
1. 有一个主页 : 使用 Servlet 动态生成
2. 有一个登录页 : 静态页面
登录页这里可以输入用户名和密码, 服务器这边验证正确性, 如果用户名和密码都正确就跳转到主页, 并 在主页显示当前用户的身份信息和当前用户的登录后的访问次数. (登录成功后就不必在登陆了)
客户端代码:
服务器代码:
验证用户名密码的 Servlet
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf8");
// 读取请求中的参数, 判定当前用户的身份信息是否正确.
String username = req.getParameter("username");
String password = req.getParameter("password");
if(username == null || username.equals("") || password == null || password.equals("")) {
resp.getWriter().write("用户名或密码不完整! 登陆失败!");
return;
}
// 验证用户名密码是否正确
if(!username.equals("zhangsan") || !password.equals("123")) {
resp.getWriter().write("用户名或密码错误! 登陆失败!");
return;
}
// 登陆成功, 创建一个 会话 (session), 把用户信息, 填写到 session 中
// 下次访问, 就可以直接从哈希表中查询出来
HttpSession session = req.getSession(true);
session.setAttribute("username", "zhangsan");
// 先取出来判断, 为 null 登陆次数才设为 0
Integer visitCount = (Integer) session.getAttribute("visitCount");
if(visitCount == null) {
// 登录次数初始为 0
session.setAttribute("visitCount", 0);
} else{
// 不做任何处理
}
// 让页面跳转到登录页面,这里的重定向跳转, 是 GET 请求
resp.sendRedirect("index");
}
}
主页 Servlet
@WebServlet("/index")
public class IndexServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 要做的工作, 就是把当前的用户信息, 给展示到页面上
HttpSession session = req.getSession(false);
if(session == null) {
// 用户未登录, 则跳转到登录页面, 要求用户重新登陆
resp.sendRedirect("login.html");
return;
}
// 已经登陆成功, 就获取到 会话 中的数据
// 由于 getAttribute 返回的是 Object, 所以需要强转
String username = (String) session.getAttribute("username");
Integer visitCount = (Integer) session.getAttribute("visitCount");
visitCount = visitCount + 1;
session.setAttribute("visitCount", visitCount);
// 显示用户和访问次数
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前用户为: " + username + " 访问次数: " + visitCount);
}
}
下图是用户登录一次后,后续访问服务器的操作, 只要服务器不重启, session 的哈希表就会一直存在.
如果是未登录状态,输入用户名和密码后, 则是创建一个新的 session 对象, 然后存储用户的身份信息.
结合 fiddler 抓包进行理解:
第一次请求: 获取到 login.html 页面
初始情况下请求中没有 Cookie,响应中也没有 Set-Cookie
请求报文
第二次请求: 输入用户名密码, 点击登录
此次请求中也没有 Cookie ,但是响应报文中会带有 Set-Cookie. Set-Cookie 中 key 的名字叫做 JSESSIONID (Servlet 自动生成的), value 就是一串很长的十六进制数字, 是由服务器生成的 sessionId.
响应报文
第三次请求: 登录成功之后, 自动重定向访问主页
此时请求中带有 Cookie, 这个 Cookie就是上次 Set-Cookie 中的内容, 只要不重启服务器, 后续再反复请求, 请求中就都会带有这个 Cookie 了.