会话技术Cookie&Session
1. 会话: 一次会话中包含多次请求和响应.
* 一次会话: 浏览器第一次给服务器资源发送请求, 会话建立, 知道有一方断开为止
2. 功能: 在一次会话的范围内的多次请求键, 共享数据
3. 方式:
1. 客户端会话技术: Cookie
2. 服务器端会话技术: Session
Cookie
1. 概念: 客户端会话技术, 将数据保存到客户端.
2. 快速入门:
* 使用步骤:
1. 创建Cookie对象, 绑定数据
* new Cookie(String name, String value)
2. 发送Cookie对象
* response.addCookie(Cookie cookie)
3. 获取Cookie, 拿到数据
* Cookie[] request.getCookies()
4. 代码:
* CookieDemo1
Cookie cookie = new Cookie("username", "zhangsan");
response.addCookie(cookie);
* CookieDemo2
Cookie[] cookies = request.getCookies();
if(cookies != null) {
for (Cookie cookie : cookies) {
String name = cookie.getName();
String value = cookie.getValue();
System.out.println("name: " + name + " value: " + value);
}
}
* 运行结果:
name: Pycharm-abaea751 value: c588d849-c872-49ed-b318-b3fef725eeed
name: Idea-6310fadd value: bf2d1837-2817-4f2e-8e3f-888e375ac004
name: JSESSIONID value: 17EEEBEB0FF62472EDDEA353A769297D
name: username value: zhangsan
3. Cookie访问原理:
* 基于响应头set-cookie和请求头cookie实现
* 第一次请求CookieDemo1:
* 访问服务器, 会话建立, 服务器创建cookie, 设置cookie, 返回cookie给浏览器客户端, 浏览器默认将cookie本地存储到浏览器.
* 第二次请求CookieDemo2:
* 浏览器默认携带cookie, 访问服务器, 所以服务器能拿到cookie.
.....
4. cookie的细节
1. 一次可不可以发送多个cookie?
* 可以
* 可以创建多个Cookie对象, 使用response使用多次addCookie方法发送cookie即可.
* 代码:
Cookie c1 = new Cookie("gender", "male");
Cookie c2 = new Cookie("age", "18");
response.addCookie(c1);
response.addCookie(c2);
2. cookie在浏览器中保存多长时间?
1. 默认情况, 当浏览器关闭后, Cookie数据被销毁
2. 持久化存储:
* setMaxAge(int seconds)
1. 正数: 将Cookie数据写到硬盘的文件中. 持久化存储. cookie存活时间.
2. 负数: 默认值
3. 零: 删除Cookie信息
4. 代码:
Cookie cookie = new Cookie("msg", "setMaxAge");
cookie.setMaxAge(0);
response.addCookie(cookie);
3. cookie能不能存中文?
* 在tomcat 8 之前, cookie中不能直接存储中文数据.
* 需要将中文数据转码---一般采用URL编码(%E3)
* 在tomcat 8 之后, cookie支持中文数据. 特殊字符还是不支持, 建议使用URL编码存储, URL解码解析.
* 代码:
Cookie cookie = new Cookie("msg", "你好");
response.addCookie(cookie);
* 取的时候还是和原来一样的取法.
4. cookie共享问题?
1. 假设在一个tomcat服务器中, 部署了多个web项目, 那么在这些web项目中cookie能不能共享?
* 默认情况下cookie不能共享
* 就是代码中不使用: setPath
* setPath(String path): 设置cookie的获取范围, 默认情况下, 设置当前的虚拟目录
* 如果要共享, 则可以将path设置为"/"
* 代码:
Cookie cookie = new Cookie("msg", "你好");
cookie.setPath("/");
response.addCookie(cookie);
2. 不同的tomcat服务器间cookie共享问题?
* setDomain(String path): 如果设置一级域名相同, 那么多个服务器之间cookie可以共享
* setDomain(".baidu.com"), 那么tieba.baidu.com和news.baidu.com中的cookie可以共享
5. Cookie的特点和作用
1. cookie存储数据在客户端浏览器
2. 浏览器对于单个cookie 的大小有限制(4kb) 以及 对同一个域名下的总cookie数量也有限制(20个)
* 作用:
1. cookie一般用于存储少量的不太敏感的数据
2. 在不登入的情况下, 完成服务器对客户端的身份识别.
6. 案例: 记住上一次访问时间
1. 需求:
1. 访问一个Servlet,如果是第一次访问,则提示:您好,欢迎您首次访问。
2. 如果不是第一次访问,则提示:欢迎回来,您上次访问时间为:显示时间字符串
2. 分析:
1. 可以采用Cookie完成
2. 在服务器中的Servlet判断是否有一个名为lastTime的cookie
1. 有: 不是第一次访问
1. 响应数据: 欢迎回来, 您上次访问时间为:2020年01月30日 16:31:26
2. 协会Cookie: lastTime=2020年01月30日 16:31:26
2. 没有: 是第一次访问
1. 响应数据: 您好, 欢饮您首次访问
2. 写回Cookie: lastTime=2020年01月30日 16:31:26
3. cookie 存储的 名称 相同 会被 替换掉
4. cookie 存储特殊字符, 需要编码存储
5. 代码:
response.setContentType("text/html;charset=utf-8");
Cookie[] cookies = request.getCookies();
boolean flag = false;
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("lastTime".equals(cookie.getName())) {
flag = true;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date date = new Date();
String dateFormat = sdf.format(date);
System.out.println("编码前: " + dateFormat);
dateFormat = URLEncoder.encode(dateFormat, "utf-8");
System.out.println("编码后: " + dateFormat);
cookie.setValue(dateFormat);
cookie.setMaxAge(300);
response.addCookie(cookie);
String value = cookie.getValue();
System.out.println("解码前: " + value);
value = URLDecoder.decode(value, "utf-8");
System.out.println("解码后: " + value);
response.getWriter().write("欢迎回来, 您上次访问时间为:" + value);
return;
}
}
}
if (cookies == null || cookies.length == 0 || flag == false) {
Cookie cookie = new Cookie("lastTime", "xxx");
cookie.setMaxAge(300);
response.addCookie(cookie);
response.getWriter().write("您好, 欢迎您首次访问");
}
Session
1. 概念: 服务器端会话技术, 在一次会话的多次请求间共享数据, 将数据保存在服务器端的对象中. HttpSession
2. 快速入门:
1. 获取Session对象:
* HttpSession session = request.getSession();
2. HttpSession对象:
Object getAttribute(String name): 从session中获取该键的值
void setAttribute(String name, Object value): 存储键值对到session中
void removeAttribute(String name):删除session存储的键值对
void invalidate(): 销毁session
3. 代码:
* SessionDemo1:
HttpSession session = request.getSession();
session.setAttribute("msg", "hello session");
* SessionDemo2:
HttpSession session = request.getSession();
Object msg = session.getAttribute("msg");
3. 原理:
* Session的实现是依赖于Cookie的.
* 两个获取的session对象是同一个
* SessionDemo1第一次获取Session, 没有Cookie, 会在内存中创建一个新的Session对象, 并给这个Session对象设置了一个JSESSIONID为xxx, 响应给客户端set-cookie: JSESSIONID=xxx.
* 当浏览器请求SessionDemo2的时候会自动携带一个请求头: cookie: JSESSIONID=xxx, 当在此获取Session对象时, 发现内存中有一个JSESSIONID为xxx, 直接把这个session给返回回去了. 所以一次会话内, 多次请求的Session对象时同一个.
4. 细节:
1. 当客户端关闭后, 服务器不关闭, 两次获取session是否为同一个?
* 默认情况下. 不是.
* 如果需要相同, 则可以创建cookie, 键为JSESSIONID, 设置最大存活时间, 让cookie持久化保存
* 代码:
HttpSession session = request.getSession();
System.out.println(session);
Cookie cookie = new Cookie("JSESSIONID", session.getId());
cookie.setMaxAge(60 * 60);
response.addCookie(cookie);
2. 客户端不关闭, 服务器关闭后, 两次获取的session是否为同一个?
* 不是同一个, 但是要确保数据不丢失
* session的钝化:
* 在服务器正常关闭之前, 将session对象系列化到硬盘上
* session的活化:
* 在服务器启动后, 将session文件转化为内存中的session对象即可.
* session的钝化和活化, tomcat自动帮我们完成了, 所以虽然session对象不是同一个, 但是数据不会丢失.
* IDEA只能钝化不能活化, 因为IDEA服务器停止时, 会在work(IDEA自己的)下项目里创建SESSIONS.ser(钝化), 而当IDEA再次启动tomcat是, 它会删除work(IDEA自己的work)目录, 在创建一个新的work(IDEA自己的)目录, 所以不能实现活化, 而tomcat不会删除work(tomcat自己的), 所以可以实现活化.
* 注意: 一般部署项目都会部署在tomcat上, 而不是IDEA上, 所以不用担心这个问题.
3. session的失效时间?
1. 服务器关闭
2. session对象调用invalidate()方法
3. session默认失效时间, 30分钟
* 选择性配置修改:
* tomcat里的conf/web.xml找到session-config修改时间 单位 分钟
<session-config>
<session-timeout>30</session-timeout>
</session-config>
5. 案例: 用户登入
1. 案例需求:
1. 访问带有验证码的登录页面login.jsp
2. 用户输入用户名,密码以及验证码。
* 如果用户名和密码输入有误,跳转登录页面,提示:用户名或密码错误
* 如果验证码输入有误,跳转登录页面,提示:验证码错误
* 如果全部输入正确,则跳转到主页success.jsp,显示:用户名,欢迎您
2. 代码(这里我只附上了逻辑代码, 验证码的servlet就不附上了, 还有用户类, 数据库连接池的工具类, 查询数据库的类, 都不附上了):
* 登入逻辑的代码
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
Map<String, String[]> userMap = request.getParameterMap();
User user = new User();
try {
BeanUtils.populate(user, userMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
String checkCode = request.getParameter("checkCode");
if ("".equals(checkCode) || "".equals(user.getUsername()) || "".equals(user.getPassword())) {
request.setAttribute("param_error", "参数不完整...");
request.getRequestDispatcher("/login.jsp").forward(request, response);
return;
}
HttpSession session = request.getSession();
String code = (String)session.getAttribute("checkCode");
session.removeAttribute("checkCode");
if (code == null || !(code.equalsIgnoreCase(checkCode))) {
request.setAttribute("cc_error", "验证码错误");
request.getRequestDispatcher("/login.jsp").forward(request, response);
return;
}
User loginUser = new UserDao().login(user);
if (loginUser == null) {
request.setAttribute("login_error", "用户名或密码错误");
request.getRequestDispatcher("/login.jsp").forward(request, response);
return;
}
session.setAttribute("user", loginUser);
response.sendRedirect(request.getContextPath() + "/success.jsp");