文章目录
- 为什么需要会话控制
- 带来的问题
- 如何解决无状态的问题——Cookie
- 如果只靠单纯的Cookie存在的问题
- 单纯Cookie导致问题的解决方法——Session
- Session
- session的结构
- 一些关于Session的API
- Session的保存作用域
- Cookie时效性
- 会话和持久化Cookie对比
- Cookie的domain和path
为什么需要会话控制
Http是无状态的协议
- HTTP 无状态 :服务器无法判断这两次请求是同一个客户端发过来的,还是不同的客户端发过来的
- 无状态的意思换句话说,服务器记不住你,可能你每刷新一次网页,就要重新输入一次账号密码进行登录。这显然是让人无法接受的
- 用生活中的例子来说
- 比如我们A(服务器)这个人开了一家商店,A这个人有一个特点,就是非常健忘,它的记忆只能维持到一个用户提出购买请求,和服务结束这一段时间
带来的问题
- 我们可能会有一些需求,比如我们需要区分不同请求-响应,请求响应是不是同一个用户发送的,比如用户购买的越多,折扣就越大,那我们就需要分辨出不同的请求响应之间的逻辑关系,所以引入cookie体系
- 或者无状态态带来的现实问题:第一次请求是添加商品到购物车,第二次请求是结账;如果这两次请求服务器无法区分是同一个用户的,那么就会导致混乱
- 通过会话跟踪技术来解决无状态的问题。
保持用户登录状态,背后的底层逻辑是:服务器在接收到用户请求的时候,有办法判断这个请求来自于之前的某一个用户。所以保持登录状态,本质上是保持**『会话状态』**
如何解决无状态的问题——Cookie
以生活举例子
- Cookie:就相当于A(服务器)发给每个客户(客户端)的一个凭证,客户必须有保存这个凭证的责任(你丢了,我就不知道你是不是你了),也必须在每次接收服务的时候带着这个凭证(你不带,我也不知道你是不是你)
- 发凭证:从服务器到客户端,所以肯定是在响应体中,Set-Cookie
- 携带凭证:从客户端到服务器,所以肯定在请求体中 Cookie
- 我说的是「一个」cookie 可以认为是一个变量,形如 name=value,但是服务器可以一次设置多个 cookie,所以有时候说 cookie 是「一组」键值对儿,这也可以说得通。
具体的实现
使用HttpSession对象,将数据存入会话域就能保持会话状态。
HttpSession session = request.getSession();
session.setAttribute("user", user);
具体实例证明
//演示Cookie
public class Demo3 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取session,如果获取不到,则创建一个新的
Cookie cookie1=new Cookie("name","lsc");
Cookie cookie2=new Cookie("age","22");
response.addCookie(cookie1);
response.addCookie(cookie2);
}
}
第一次在浏览器上访问demo3
- 我们看到了在服务器中设置的Cookie值我们返回给了服务端,存储在Response Headers——实现了对应的发凭证的操作
第二次在同一个浏览器上访问demo3
- 第二次浏览器发送demo3请求,我们的请求中的请求头中携带对应的Cookie值——完成了携带凭证
如果只靠单纯的Cookie存在的问题
安全问题
:如果我们把cookie完全交给客户端,我们服务器完全信任cookie,那么如果碰到恶意用户或者恶意程序进行修改伪造别人的cookie,那么是不安全的性能问题
:我们也知道现在的很多网站功能很复杂,而且涉及很多的数据交互,比如说电商网站的购物车功能,信息量大,而且结构也比较复杂,无法通过简单的 cookie 机制传递这么多信息,而且要知道 cookie 字段是存储在 HTTP header 中的,就算能够承载这些信息,也会消耗很多的带宽,比较消耗网络资源。- 因为我们上面的Cookie只是为了让服务器知道我是谁
单纯Cookie导致问题的解决方法——Session
- 解决方法:拆分保存数据,把敏感的数据保存到服务器的内部,
cookie只携带不敏感的信息,通常来说就是一个卡号也就是我们的SessionID
,也就引入我们的session机制
- 就类似我们服务器来保存重要的数据(洗浴),客户端来拿着卡号去对应的柜子去取东西(我们的随身物品)
Session
- Session:是保存在服务器的,
专属某次会话的
,一组数据,可以跨请求访问
(生命周期是跨请求的),通常Session中保存的数据也可以视为name-value.,一般cookie设置session-id.
这样客户端和服务器之间仍然使用cookie机制,只是cookie只传递id即可(cookie还是放在我们的请求头和响应头中的,存储的是session-id),大头数据全部保存在服务器
session的结构
- session类似一个卡号对应着一个柜子的结构,然后一个柜子也是一个Map的结构,因为柜子要存储name:value
一些关于Session的API
-
request.getSession()
-> 获取当前的会话,没有则创建一个新的会话-
request.getSession(true) -> 效果和不带参数相同
-
request.getSession(false) -> 获取当前会话,没有则返回null,不会创建新的
-
-
session.getId()
-> 获取sessionID -
session.isNew()
-> 判断当前session是否是新的 -
session.getMaxInactiveInterval()
-> session的非激活间隔时长,默认1800秒-
session.setMaxInactiveInterval()设置最大的非激活间隔时长
-
比如我们一些银行的网页,如果三分钟不操作,可能当前账号就需要重新登录
-
为什么Session要设置时限
- 用户量很大之后,Session对象相应的也要创建很多。如果一味创建不释放,那么服务器端的内存迟早要被耗尽。
-
设置时限的难点
- 从服务器端的角度,很难精确得知类似浏览器关闭的动作。而且即使浏览器一直没有关闭,也不代表用户仍然在使用。
-
session.invalidate()
-> 强制性让会话立即失效
Session的保存作用域
Demo4资源
//演示向HttpSession保存数据
public class Demo4 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getSession().setAttribute("uname","lina");
}
}
Demo5资源
public class Demo5 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Object unameObj = request.getSession().getAttribute("uname");
System.out.println(unameObj);
}
}
- 我们在
同一个浏览器
先去访问Demo4,然后再去访问我们的Demo5,发现输出了lina - 我们在一个浏览器去访问Demo4,然后换一个浏览器访问Demo5,发现输出的值是null
- 说明我们的session保存的信息作用域是可以跨请求的,session保存作用域是和具体的某一个session对应的
- void session.setAttribute(k,v)
- Object session.getAttribute(k)
- void removeAttribute(k)
Cookie时效性
- 会话级Cookie
- 服务器端并没有明确指定Cookie的存在时间
- 在浏览器端,Cookie数据存在于内存中
- 只要浏览器还开着,Cookie数据就一直都在
- 浏览器关闭,内存中的Cookie数据就会被释放
- 持久化Cookie
- 服务器端明确设置了Cookie的存在时间
- 在浏览器端,Cookie数据会被保存到硬盘上
- Cookie在硬盘上存在的时间根据服务器端限定的时间来管控,不受浏览器关闭的影响
- 持久化Cookie到达了预设的时间会被释放
服务器端返回Cookie时附带过期时间的响应消息头如下
服务器通知浏览器删除Cookie时的响应消息头如下
会话和持久化Cookie对比
Cookie的domain和path
上网时间长了,本地会保存很多Cookie。对浏览器来说,访问互联网资源时不能每次都把所有Cookie带上。浏览器会使用Cookie的domain和path属性值来和当前访问的地址进行比较,从而决定是否携带这个Cookie。