Cookie
和Session
会话技术:
一次会话中包含多次请求和响应,一次会话,浏览器第一次给服务器资源发送请求,会话建立,直到一方断开为止。
**功能:**在一次会话的范围内的多次请求间,共享数据
方式:
- 客户端会话技术:
Cookie
- 服务器端会话技术:
Session
Cookie
概念:客户端会话技术,将数据保存到客户端
快速入门
-
创建
Cookie
对象,绑定数据new Cookie(String name,String value);
-
发送
Cookie
对象response.addCookie(Cookie cookie);
-
获取
Cookie
,拿到数据Cookie[] request.getCookies();
@WebServlet(urlPatterns = {"/cookie1"})
public class Cookie1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String value = "hello Cookie";
// 由于Cookie不能存储特殊的字符如:空格$#等,需要转编码
// 调用
String encode = URLEncoder.encode(value, "utf-8");
// 创建Cookie对象,并且存储数据
Cookie cookie = new Cookie("message", encode);
// 发送Cookie,由于响应是Response对象的
resp.addCookie(cookie);
System.out.println("发送");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
@WebServlet(urlPatterns = {"/cookie2"})
public class Cookie2 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取出Cookie的值,获取需要使用Request对象
Cookie[] cookies = req.getCookies();
// 遍历Cookie
if (cookies!=null&&cookies.length!=0){
for (Cookie cookie:cookies) {
String name = cookie.getName();
String value = cookie.getValue();
// 由于发送Cookie时,数据封装数据时,使用编码的方式
// 需要解码
String decode = URLDecoder.decode(value,"utf-8");
System.out.println(name+" : "+decode);
}
}
}
}
Cookie
实现原理
基于响应头set-cookie
和请求头cookie
之间的通信
Cookie
的实现步骤
- 当浏览器第一次访问服务器端时,服务器会创建一个
Cookie
对象用于封装数据 - 服务器会发送一个
set-cookie
的一个头信息给浏览器 - 当浏览器接收到一个
set-cookie
头信息时,会自动将set-cookie
的信息保存到浏览器中 - 当浏览器下一次访问服务器时,会自动的携带着一个为
cookie
的头信息去访问服务器 - 服务器可以获取
cookie
头信息
可以一次发送多个Cookie
?
可以
可以创建多个`Cookie`对象,使用`respone`调用多次
`addCookie`方法发送`Cookie`即可
/**
* 演示Cookie一次性发送多个
*/
@WebServlet(urlPatterns = {"/cookie3"})
public class Cookie3 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 由于在服务器中不支持特殊字符所以需要将发送的信息进行编码,防止报错
String message = "hello Cookie";
String message_enc = URLEncoder.encode(message, "utf-8");
String username = "海康";//在tomcat8之前不支持中文,在tomcat8之后,解决中文问题,但是还是不支持特殊字符如:空格@#¥等
String username_enc = URLEncoder.encode(username,"utf-8");
// 创建多个Cookie
Cookie cookie1 = new Cookie("username", username_enc);
Cookie cookie2 = new Cookie("message", message_enc);
// 调用Response对象发送Cookie
resp.addCookie(cookie1);
resp.addCookie(cookie2);
System.out.println("Cookie发送完毕。。。");
}
}
Cookie
在浏览器中保存多长时间?
1.默认情况下,当浏览器关闭后,`Cookie`数据被销毁
2.持久化存储
调用`Cookie`中的`setMaxAge(int seconds)`方法,设置`Cookie`在浏览器中的存活时间
取值:
1.正数:将`Cookie`数据写到硬盘文件中。持久化存储,并指定`Cookie`的存活时间,时间到后,`Cookie`文件自动失效
2.负数:默认值,浏览器关闭就失效,如果服务器关闭,如果该Cookie还存活着,等服务器启动后访问还能在服务器中获取到该Cookie的值,但是会话结束了
3.零 :删除`Cookie`信息
1.注意是:将Cookie
的存活时间持久化到硬盘中,就是浏览器没有关闭,持久化到硬盘中的Cookie
如果存活时间到,访问服务器也不会获取到该Cookie
的值,因为被删除了**
2.如果服务器关闭,如果该Cookie还存活着,等服务器启动后访问还能在服务器中获取到该Cookie的值,但是会话结束了
/**
* 演示Cookie的存活时间
*/
@WebServlet(urlPatterns = {"/cookie4"})
public class Cookie4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/**
* 1.正数:将`Cookie`数据写到硬盘文件中。持久化存储,并指定`Cookie`的存活时间,时间到后,`Cookie`文件自动失效
* 2.负数:默认值
* 3.零 :删除`Cookie`信息
*/
// 创建Cookie
Cookie cookie = new Cookie("message", "hello");
// 设置Cookie的存活时间
cookie.setMaxAge(-1);//默认值就是负数,表示浏览器关闭Cookie值就失效,服务器关闭不会影响保存在浏览器中的Cookie,但是会话结束了
// cookie.setMaxAge(0);//表示删除所有Cookie的值
// cookie.setMaxAge(100);//表示将Cookie持久化到硬盘中100秒后,自动删除
// 发送Cookie
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
Cookie
能否存储中文
在`tomcat8`之前`Cookie`不能保存中文,需要将中文数据转码
一般采用`URL`编码方式
编码调用的是`URLEncoder`类中的`encode("编码字符串", "字符集[一般是utf-8]");`
解码调用的是`URLDecoder`类中的`decode("解码字符串","字符集[需要与编码字符集一致]");`//如编码是`utf-8`,解码就得是`utf-8`
编码
String username = "海康";//在tomcat8之前不支持中文,在tomcat8之后,解决中文问题,但是还是不支持特殊字符如:空格@#¥等
String username_enc = URLEncoder.encode(username,"utf-8");
// 创建多个Cookie
Cookie cookie1 = new Cookie("username", username_enc);
解码
String name = cookie.getName();
String value = cookie.getValue();
// 由于发送Cookie时,数据封装数据时,使用编码的方式
// 需要解码
String decode = URLDecoder.decode(value,"utf-8");
System.out.println(name+" : "+decode);
Cookie
数据共享问题?
同一个服务器下部署不能项目的共享问题
1.假设在一个`tomcat`服务器中,部署多个web项目,那么在这些web项目中`cookie`能不能共享数据?
默认情况下是不能的,原因是在没有设置`Cookie`对象的中`path`属性值,默认值是该项目的`虚拟目录`,所以是不能共享数据的
如果需要`Cookie`在不同项目下共享数据,需要调用`Cookie`中的`void setPath(String path)`方法,可以将`path`设置为服务器的根目录`/`
方法:
// 设置路径
public void setPath(String uri) {
this.path = uri;
}
默认情况下是不能的,原因是在没有设置Cookie
对象的中path
属性值,默认值是该项目的虚拟目录
,所以是不能共享数据的
如果需要Cookie
在不同项目下共享数据,需要调用Cookie
中的void setPath(String path)
方法,可以将path
设置为服务器的根目录/
不能服务器下部署不同项目共享数据问题
不同的`tomcat`服务器间`Cookie`共享问题?
需要调用`Cookie`对象中的`setDomain(String path);`方法,需要设置一级域名相同,达到多个服务器间的`Cookie`共享数据问题
方法:
// 设置Cookie在浏览器中存活时间
public void setMaxAge(int expiry) {
this.maxAge = expiry;
}
Cookie
的特点和作用
特点:
1.`Cookie`存储数据在客户端浏览器中
2.浏览器对于单个`Cookie`的大小有限制`(4kb)`以及对同一个域名下的总`Cookie`数量也有限制`(20个)`[浏览器不同也有差别]
作用:
1.`Cookie`一般用于存储少量的不太敏感的数据
2.在不登录的情况下,完成服务器对象客户端的身份识别
综合案例
案例需求:
1. 访问一个Servlet,如果是第一次访问,则提示:您好,欢迎您首次访问。
2. 如果不是第一次访问,则提示:欢迎回来,您上次访问时间为:显示时间字符串
Cookie
源码区
public class Cookie implements Cloneable, Serializable {
private static final long serialVersionUID = -6454587001725327448L;
private static final String TSPECIALS;
private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");
private String name;
private String value;
private String comment;
private String domain;//一级域【一般用于在不同服务器中部署的不同项目之间的数据共享】
private int maxAge = -1;//默认值,设置Cookie在浏览器中的存活时间
private String path;//路径[一般用于设置在一个服务器上部署的多个项目之间共享数据]
private boolean secure;
private int version = 0;
private boolean isHttpOnly = false;
public Cookie(String name, String value) {
if (name != null && name.length() != 0) {
if (this.isToken(name) && !name.equalsIgnoreCase("Comment") && !name.equalsIgnoreCase("Discard") && !name.equalsIgnoreCase("Domain") && !name.equalsIgnoreCase("Expires") && !name.equalsIgnoreCase("Max-Age") && !name.equalsIgnoreCase("Path") && !name.equalsIgnoreCase("Secure") && !name.equalsIgnoreCase("Version") && !name.startsWith("$")) {
this.name = name;
this.value = value;
} else {
String errMsg = lStrings.getString("err.cookie_name_is_token");
Object[] errArgs = new Object[]{name};
errMsg = MessageFormat.format(errMsg, errArgs);
throw new IllegalArgumentException(errMsg);
}
} else {
throw new IllegalArgumentException(lStrings.getString("err.cookie_name_blank"));
}
}
//设置注释
public void setComment(String purpose) {
this.comment = purpose;
}
//获取注释
public String getComment() {
return this.comment;
}
//设置一级域名,用于在不同服务器中部署的不同项目之间共享数据
public void setDomain(String domain) {
this.domain = domain.toLowerCase(Locale.ENGLISH);
}
//获取一级域名
public String getDomain() {
return this.domain;
}
// 设置Cookie在浏览器中存活时间
public void setMaxAge(int expiry) {
this.maxAge = expiry;
}
// 获取Cookie的存活时间
public int getMaxAge() {
return this.maxAge;
}
// 设置路径
public void setPath(String uri) {
this.path = uri;
}
// 获取路径
public String getPath() {
return this.path;
}
public void setSecure(boolean flag) {
this.secure = flag;
}
public boolean getSecure() {
return this.secure;
}
// 获取Cookie的名称
public String getName() {
return this.name;
}
// 设置Cookie中的值
public void setValue(String newValue) {
this.value = newValue;
}
// 获取Cookie中的值
public String getValue() {
return this.value;
}
// 获取版本号
public int getVersion() {
return this.version;
}
// 设置版本号
public void setVersion(int v) {
this.version = v;
}
private boolean isToken(String value) {
int len = value.length();
for(int i = 0; i < len; ++i) {
char c = value.charAt(i);
if (c < ' ' || c >= 127 || TSPECIALS.indexOf(c) != -1) {
return false;
}
}
return true;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException var2) {
throw new RuntimeException(var2.getMessage());
}
}
public void setHttpOnly(boolean isHttpOnly) {
this.isHttpOnly = isHttpOnly;
}
public boolean isHttpOnly() {
return this.isHttpOnly;
}
static {
if (Boolean.valueOf(System.getProperty("org.glassfish.web.rfc2109_cookie_names_enforced", "true"))) {
TSPECIALS = "/()<>@,;:\\\"[]?={} \t";
} else {
TSPECIALS = ",; ";
}
}
}
Session
重点
概念:服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中
快速入门
1.获取HttpSession
对象
调用`Request`对象中的`getSession()`方法获取一个`HttpSession`对象
2.使用HttpSession
对象共享数据常用方法
获取共享的数据
Object getAttribute(String name)
设置共享的数据
void setAttribute(String name, Object value)
删除共享的数据
void removeAttribute(String name)
这三个方法与Request
对象和ServletContext
对象中的三个方法一样,只是操作的对象不同,这三个对象都是用于共享数据的,只是作用域不同
案例:
/**
* @Author 海康
* @Version 1.0
*
* Session的快速入门
* 说明Session是在一次会话之中共享数据的
*/
@WebServlet(urlPatterns = {"/session1"})
public class Session1 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取 HttpSession 对象
HttpSession session = req.getSession();
// 2.存储数据
session.setAttribute("message","用Session在一次会话中共享数据");
System.out.println("存储数据完毕!");
}
}
@WebServlet(urlPatterns = {"/session2"})
public class Session2 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取在一次会话中Session共享的数据
//先获取HttpSession对象
HttpSession session = req.getSession();
// 获取Session共享,根据键名获取
String message = (String) session.getAttribute("message");
System.out.println(message);
}
}
Session
原理【重之重】
Session
的实现是依赖于Cookie
的
Session
的原理
- 当浏览器访问服务器时,服务器第一次获取
Session
对象,在Cookie
中没有JSESSIONID
的值【也有可能此时没有Cookie头
,会在内存中创建一个Session
对象,并将对象Session
对应的JSESSIONID
封装到Cookie
中响应给浏览器】 - 服务器会在内存中创建一个
Session
对象,并且在内存中生成一个JSESSIONID
用于唯一标识Session
对象,并且将Session
中的值封装到一个set-cookie
头中,响应给浏览器 - 浏览器接收到一个
set-cookie
头后,将值保存在浏览器中 - 浏览器下次访问服务器地,携带着
JSESSIONID
去访问服务器,服务器根据JSESSIONID
在服务器获取对象的Session
对象 - 所以在一次会话中获取的
HttpSession
对象都是同一个
Session
细节
1.当客户端关闭后,服务器不关闭,两次获取Session
是否为同一个?
默认情况下,不是同一个`Session`对象,因为保存在客户端浏览器中的`JSESSIONID`已经被销毁了
修改`Cookie`的存活时间,如果需要相同,则可以创建一个`Cookie`,设置Cookie的键是`JSESSION`,设置最大的存活时间,让Cookie持久保存到硬盘中
代码区:
HttpSession session = req.getSession();
Cookie cookie = new Cookie("JSESSION",session.getID());
cookie.setMaxAget(60*60*24);//比如设置一天
response.addCookie(cookie);
案例:
@WebServlet(urlPatterns = {"/servlet"})
public class SessionMaxAge extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取一个Session对象
HttpSession session = req.getSession();
// 创建一个Cookie对象,用于封装数据,使用浏览器关闭后,再次请求服务器时,获取到Session还是同一个
Cookie cookie = new Cookie("JSESSIONID", session.getId());
cookie.setMaxAge(60*60*24);//设置存活时间是 24 小时
// 发送Cookie
resp.addCookie(cookie);
System.out.println(session);//打印Session地址值
}
}
2.客户端不关闭,服务器关闭后,再次获取的Session
是同一个吗?
在默认情况下,不是同一个
但是要确保数据不丢失,需要对`Session`钝化【就是序列化】和`Session`活化【就是反序列化】
`Session`的钝化:
在服务器正常关闭之前,将`session`对象系列化到硬盘上
`Session`的活化
在服务器启动后,将`session`文件转化为内存中的`session`对象即可
上述的钝化和活化tomcat
自动完成了以下的工作了
Seesion
什么时候被销毁,有三种情况
情况一:
服务器关闭时,服务器关闭后,保存在内存中的`Session`会被销毁
情况二:
可以调用`Session`中的`invalidate()`,将服务器端`Session`销毁掉
情况三:可以在`web.xml`文件中配置`Session`的销毁时间
<session-config>
<session-timeout>30</session-timeout>//默认情况下是30分钟,可以修改该值
</session-config>
Session
的特点
Session
用于存储一次会话的多次请求的数据,存在服务器端Session
可以存储任意类型,任意大小的数据
Session
与Cookie
的区别
Session
存储数据在服务器,Cookie
在客户端Session
没有数据大小的限制,Cookie
有大小限制【4kb
】Session
数据安全【在服务器端防护相对高】,Cookie
相对是不安全的
Session
源码
public interface HttpSession {
long getCreationTime();//获取创建时间
String getId();//获取Session的ID
long getLastAccessedTime();
ServletContext getServletContext();//获取ServletContext对象
void setMaxInactiveInterval(int var1);
int getMaxInactiveInterval();
/** @deprecated */
@Deprecated
HttpSessionContext getSessionContext();
Object getAttribute(String var1);
/** @deprecated */
@Deprecated
Object getValue(String var1);
Enumeration<String> getAttributeNames();
/** @deprecated */
@Deprecated
String[] getValueNames();
void setAttribute(String var1, Object var2);
/** @deprecated */
@Deprecated
void putValue(String var1, Object var2);
void removeAttribute(String var1);
/** @deprecated */
@Deprecated
void removeValue(String var1);
void invalidate();//销毁Session对象方法,谁调用销毁谁
boolean isNew();
}