Servlet 能够给我们提供两部分数据,一个是 Servlet 初始化时设置的 ServletConfig,这个类基本涵盖了 Servlet 本身 和 Servlet 运行的容器的基本信息,ServletConfig 的实际对象是 StandardWrapperFacade,看看接口就可以知道有哪些信息。
还有一部分数据是 ServletRequest 类提供,实际对象是 RequestFacade,主要描述请求的 HTTP 协议信息,所以要掌握 Servlet 的工作方式,必须要清楚 HTTP 协议。关于这一块,还有 Session 与 Cookie。
Session 与 Cookie 的作用都是为了保持访问用户与后端服务器的交互状态。Session 和 Cookie 的使用是矛盾的,例如,使用 Cookie 传递信息时,假如每个 Cookie 占200字节,如果有一天有几亿用户,需要多少带宽。所以大访问量时,希望用 Session,但是 Session 的弱点是不容易在多态服务器之间共享。
不管 Session 和 Cookie 有什么不足,还是需要他们,Session 如何基于 Cookie 工作,三种方式能让 Session 正常工作:
- 基于 URL Path Parameter,
- 基于 Cookie,如果没有修改 Context 容器的 Cookies 标识,默认支持
- 基于 SSL,默认不支持,只有 connector.getAttribute(“SSLEnabled”) 为 true 时才支持
第一种情况下,当浏览器不支持 Cookie 功能时,浏览器会将用户的 SessionCookieName 重写到用户请求的 URL 参数中,它的传递格式如 /path/Servlet?name=value&name2=value2& Name3=value3,其中“Servlet;”后面的 K-V 对就是要传递的 Path Parameters,服务器会从这个 Path Parameters 中拿到用户配置的 SessionCookieName。关于这个 SessionCookieName,如果你在 web.xml 中配置 session-config 配置项的话,其 cookie-config 下的 name 属性就是这个 SessionCookieName 值,如果你没有配置 session-config 配置项,默认的 SessionCookieName 就是大家熟悉的“JSESSIONID”。接着 Request 根据这个 SessionCookieName 到 Parameters 拿到 Session ID 并设置到 request.setRequestedSessionId 中。
请注意如果客户端也支持 Cookie 的话,Tomcat 仍然会解析 Cookie 中的 Session ID,并会覆盖 URL 中的 Session ID。
如果是第三种情况的话将会根据 javax.servlet.request.ssl_session 属性值设置 Session ID。
有了 Session ID 服务器就会根据它创建一个 HttpSession 对象了,第一次触发是通过 request.getSession() 方法,如果当前的 Session ID 还没有对应的 HttpSession 对象就创建一个新的,并将这个对象加到 org.apache.catalina.Manager 的 sessions 容器中保存,Manager 类将管理所有的 Session 生命周期,过期的 Session 将会被回收,断开与客户端的连接,Session 将被序列化到磁盘等。只要 HttpSession 对象存在,用户就可以根据 Session ID 来获取到这个对象。也就达到了状态的保持。
HttpSession 对象实际上是 StandardSession 对象的门面对象,这与前面的 Request 和 Servlet 一样。
与 Session 关联的 Cookie 与其他 Cookie 没有什么不同,这个属性的配置可以通过 web.xml 中的 session-config 配置来指定。
Http 无状态协议
Http 无状态协议,也就是没有记忆力,服务器端不知道上一次是哪个客户端请求了自己
问题:
在一次会话中,多个请求之间无法共享数据,无法跟踪用户的会话信息
Cookie
HTTP Cookie(也叫Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。
用途:
- 会话状态管理 (用户登录状态、购物车、游戏分数或其他需要记录的信息)
- 个性化设置(用户自定义设置、主题等)
- 浏览器行为跟踪(跟踪分析用户行为)
Cookie曾一度用于客户端数据的存储,因当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie渐渐被淘汰。由于服务器指定Cookie后,浏览器的每次请求都会携带Cookie数据,会带来额外的性能开销(尤其是在移动环境下)。新的浏览器API已经允许开发者直接将数据存储到本地,如使用
Cookie 的使用
创建:
Cookie cookie = new Cookie(String name, String value);
name 是唯一标识,value 是要存的数据如何把 cookie 响应给服务器
- response对象.addCookie(cookie);
在服务端如何读取 Cookie 信息
- Cookie[] cookies = req对象.getCookie();
Cookie 中文问题
- 解决:把中文转非中文,读取时再转成中文
- 工具类:URLEncoder、URLDecoder
Cookie的分类
- 直接创建一个同名的新 Cookie,注意:需要重新把 Cookie 响应给浏览器覆盖旧的 Cookie
- 直接修改 Cookie 的值,需要重新把 Cookie 响应给浏览器去覆盖旧的 Cookie
Cookie 分类
- 会话 Cookie (默认),只能一个会话中有效果
- 持久 Cookie,可以设置存活时间
- setMaxAge(int expiry),设置最大年龄
- expiry,负数:会话 Cookie;0:删除:删除 Cookie;正数:Cookie的有效时间(秒)
删除 Cookie
setMaxAge(0)
1 | Cookie cookie = new Cookie("user", "suntao"); |
cookie的存活期:默认为-1
会话Cookie:把Cookie保存到浏览器上,当存活期为负数
持久Cookie:把Cookie保存到文件中,当存活期为正数
设置存活期:c.setMaxAge();
使用 EL 表达式获取 Cookie
使用
${cookie.user}
获取之前存入的cookie,${cookie.user.name}
获取 cookie 的名称 -> user${cookie.user.value}
获取之前存入的 cookie 值。-> suntao
设置路径和域
Cookie 的 domain 和 path
- domain 表示的是 cookie 所在的域,默认为请求地址,如 网址为 www.text.com/text/text.aspx,那么 domain 默认为 www.test.com。
- 跨域访问,如域A 为 t1.test.com,域B 为 t2.test.com,在域A 生产一个令 A 和 B 都能访问的 cookie 就要将该 cookie 的 domain 设置为 .test.com
- 如果要在域A 生产一个令域A 不能访问而域B 可以访问的 cookie 就要将该 cookie 的 domain 设置为 t2.test.com
- path 表示 cookie 所在的目录,asp.net 默认为/,就是根目录,在同一个服务器上有目录如下:
/test/
,/test/cd/
,/test/dd/
,现设一个 cookie1 的 path 为/test/
。 cookie2 的 path 为/test/cd/
,那么 test 下的所有页面都可以访问到 cookie1,而/test/
和/test/dd
下的子页面不能访问 cookie2,这是因为 cookie 能让其 path 路径下的页面访问- 如果设置 cookie 的 path 为
/
,cookie.setPath("/")
表示当前域下的任何请求连接都可以共享 cookie。 - 默认创建 cookie 的Path是 cookie 的父级目录
- 如果设置 cookie 的 path 为
- 浏览器会将 domain 和 path 都相同的 cookie 保存在一个文件里,cookie间用*隔开
- 含键值对的 cookie: 如果 cookie 含多个键值对,cookie 的格式是
name=key1=value1&key2=value2...
,可以理解为单键值对的值保存一个自定义的多键值字符串。
cookie 的限制
- 不安全
- Cookie存储中文比较麻烦
- 一个Cookie只能存储一个数据,而且是String
- 一台服务器在一个客户端存储的 Cookie 大小和数量有限
- Cookie大小在4kb之内
- 一台服务器在一个客户端最多保存20个cookie
- 一个浏览器最多可以保存300个cookie
Session
服务器端技术,运行时,可以为每一个用户的浏览器创建一个其独享的 session 对象。用户在访问服务器的 web 资源时,可以把各自的数据放在各自的 session 中。
session 的获取
- request对象.getSession(true)
- true 表示判断是否有 session 对象,如果有则返回,没有会创建一个新的 session 再返回
- 如果是 false 如果有则返回,没有会返回 null
- getSession() 同 true
session对象的常用方法
- session对象.setAttribute(String name, Object val); 设置
- session对象.getAttribute(String name); 获取数据
- session对象.removeAttribute(String name); 移除数据
- session对象.invalidate(); 销毁 session 对象
默认: web.xml 中配置了 30 分钟有效期,void setMaxInactiveInterval(int interval)
设置失效时间(秒)
session URL重写(解决禁cookie)
当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。
保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID。但cookie可以被人为的禁止,则必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。
经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面。还有一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如:
1 | <form name="testform" action="/xxx"> |
服务器可以通过 url 重写的方式来传递 SessionID 的值,并不是完全依赖 Cookie,如果客户端 Cookie 禁用,服务器可以自动通过重写 URL 的方式来保存 Session 的值,并且这个过程对程序员透明。
即使不写 cookie,在使用 request.getCookie() 取出的 Cookie数组 的长度也是1,而这个Cookie的名字就是
JSESSIONID
,还有一个很长的二进制字符串,是 SessionID 的值
如果有浏览器拒收cookie,则使用在url后面拼接 jsessionid 的方式将 sessionid 传递给服务器
URL 重写 是通过向 URL 连接添加参数,把 session ID 作为只包含在链接中,为了使这生效,你需要为你的 servlet 响应部分的每个连接 添加 session ID
http://www.test.com/test;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
response.encodeURL(url) 可以在 url 后面拼接 jsessionid属性。
href='${pageContext.response.encodeURL("/session/list")}'
给 a 标签跳转时url附带 jsessionid 属性。
如果浏览器有接受 cookie,则不带 jsessionid
## session 规范
- Session 中属性名(XX_IN_SESSION)
- 存放多个属性可调用多次 setAttribute,一般把数据封装成对象存储 Session 中
- 多服务器共享 Session,Session中的对象类型需要实现 java.io.Serializable 接口
- 序列化:把对象信息存储为二进制
- 反序列化:把二进制信息回复称对象