会话
什么是会话
会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。
会话过程中要解决的一些问题?
每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,程序要想办法为每个用户保存这些数据。
例如:用户点击超链接通过一个servlet购买了一个商品,程序应该想办法保存用户购买的商品,以便于用户点结帐servlet时,结帐servlet可以得到用户购买的商品为用户结帐。
思考:用户购买的商品保存在request或servletContext中行不行?
保存会话数据的两种技术
Cookie
Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。
Session
Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的HttpSession对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务
Cookie API
javax.servlet.http.Cookie
javax.servlet.http.Cookie类用于创建一个Cookie
response接口中定义了一个addCookie方法,它用于在其响应头中增加一个相应的Set-Cookie头字段。
request接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。
Cookie类的方法:
public Cookie(String name,String value)
setValue与getValue方法
setMaxAge与getMaxAge方法 (秒)
setPath与getPath方法
setDomain与getDomain方法
getName方法
Cookie类的一些属性
Cookie应用场景
记录上次访问时间
package com.gyf.web.servlet.lesson09;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import com.gyf.model.User;
@WebServlet("/Lesson09Servlet01")
public class Lesson09Servlet01 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//Cookie应用场景:记录上次访问时间
//设置响应时乱码的问题
response.setHeader("content-type", "text/html;charset=utf-8");
//获取请求头的Cookie
Cookie[] cks = request.getCookies();
if(cks != null){
for(Cookie c : cks){
System.out.println(c.getName() + ":" + c.getValue());
response.getWriter().write("上次访问的时间:" + c.getValue());
}
}
//1.创建一个Cookie对象
Cookie ck = new Cookie("lastAccessTime",System.currentTimeMillis() + "");
//2.设置cookie的存活时间【单位是秒】
ck.setMaxAge(60 * 5);
//3.把cookie返回给客户端【通过响应头传给客户端】
response.addCookie(ck);
}
}
Cookie的maxAge方法
设置cookies的存活时间
maxAge:cookie的缓存时间。默认是-1,默认存在浏览器的缓存中。单位是秒
负数:表示cookie的数据存在浏览器缓存中
0:表示删除cookie
正数:缓存在持久化磁盘上的时间
Cookie细节
一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。
浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。
如果创建了一个cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。若希望浏览器将该cookie存储在磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间。将最大时效设为0则是命令浏览器删除该cookie。
记住用户名
package com.gyf.web.servlet.lesson11;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/Lesson11LoginServlet")
public class Lesson11LoginServlet extends HttpServlet{
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setHeader("content-type","text/html;charset=utf-8");
//1.获取表单请求参数
String username = request.getParameter("username");
String password = request.getParameter("password");
String remember = request.getParameter("remember");
//2.简单判断是否登陆成功
boolean success = "gyf".equals(username) && "123".equals(password);
if(success){
response.getWriter().write("登陆成功");
}else{
response.getWriter().write("登陆失败");
}
//3.把remeber和username存在cookie中
if("true".equals(remember) && success == true){
Cookie ck1 = new Cookie("remember",remember);
ck1.setMaxAge(60 * 60);//存活1个小时
response.addCookie(ck1);
Cookie ck2 = new Cookie("username",username);
ck2.setMaxAge(60 * 60);//存活1个小时
response.addCookie(ck2);
}else{
//清除cookie
Cookie[] cks = request.getCookies();
if(cks != null){
for(Cookie c: cks){
c.setMaxAge(0);//删除cookie
response.addCookie(c);
}
}
}
}
}
<%@ page language="java" contentType="text/html; UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
/*1.通过cooki的key来获取值*/
function getCookieValue(key) {
//1.通过js来获取cookies
var cookie = document.cookie;//remember=true; remember=true
//2.拆分cookie
var cks = cookie.split('; ');
for(var i = 0; i<cks.length;i++){
var ck = cks[i];//remember=true 或者 remember=true
//3.取name和value
var ckArr = ck.split("=");
var name = ckArr[0];
var value = ckArr[1];
//4.找到对应的key的value
if(key == name){
return value;
}
}
}
window.onload = function(){
var remember = getCookieValue('remember');
if(remember == 'true'){
//1.给记住用户名勾选中
var rememberTag = document.getElementById('remember');
rememberTag.checked = 'checked';
//2.给用户名显示上次登陆名字
var username = getCookieValue('username');
var usernameTag = document.getElementById('username');
usernameTag.value = username;
}
}
var v1 = getCookieValue('remember');
var v2 = getCookieValue('username');
console.log(v1);
console.log(v2);
</script>
</head>
<body>
<form action="/day10-20180321/Lesson11LoginServlet" method="post">
<table border="1">
<tr>
<td>用户名:</td>
<td><input id="username" type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2">
<input id="remember" type="checkbox" name="remember" value="true">记住用户名
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="登陆">
</td>
</tr>
</table>
</form>
</body>
</html>
历史记录
package com.gyf.web.servlet.lesson01;
import java.util.HashMap;
import java.util.Map;
import com.gyf.model.Book;
public class DBUtils {
private static Map<Integer, Book> books = new HashMap<>();
//公用的数据写在一个静态集合,赋值一次的内容最好写在static代码块中
static{
books.put(1, new Book(1,"吉他弹奏入门","lp","168.88"));
books.put(2, new Book(2,"锁呐弹奏入门","hg","168.88"));
books.put(3, new Book(3,"军事杂志","wf","168.88"));
books.put(4, new Book(4,"帅哥杂志","sgx","168.88"));
books.put(5, new Book(5,"美女杂志","xd","168.88"));
}
//返回所有书
public static Map<Integer, Book> getAllBooks(){
return books;
}
//通过id找到书
public static Book findBookById(Integer id){
return books.get(id);
}
}
package com.gyf.web.servlet.lesson13;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.gyf.model.Book;
@WebServlet("/ShowAllBookServlet")
public class ShowAllBookServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setHeader("content-type","text/html;charset=utf-8");
//1.准备一些书的数据
List<Book> books = new ArrayList<>();
/*books.add(new Book(1,"吉他弹奏入门","lp","168.88"));
books.add(new Book(2,"锁呐弹奏入门","hg","168.88"));
books.add(new Book(3,"军事杂志","wf","168.88"));
books.add(new Book(4,"帅哥杂志","sgx","168.88"));
books.add(new Book(5,"美女杂志","xd","168.88"));*/
//2.显示书的数据给浏览器
PrintWriter writer = response.getWriter();
writer.write("图书列表<br>");
/*
* <a href='/项目名/ViewBookServlet?id=1'>书名</a>
* Servlet返回给客户端的数据可以是html标签数据
*/
for(Entry<Integer, Book> entry : DBUtils.getAllBooks().entrySet()){
Book book = entry.getValue();
String aStr = "<a href='http://localhost:8080" + request.getContextPath() + "/ViewBookServlet?id="+ book.getId() +"'>"+ book.getName() + "</a><br>";
writer.write(aStr);
}
}
}
package com.gyf.web.servlet.lesson01;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.gyf.model.Book;
@WebServlet("/ViewBookServlet")
public class ViewBookServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 常识:从浏览器/客户端获取的所有数据都是字符串类型
*/
response.setHeader("content-type","text/html;charset=utf-8");
//1.获取书的id
String str_id = request.getParameter("id");
//2.自己把字符串转成int
int id = Integer.parseInt(str_id);
PrintWriter writer = response.getWriter();
writer.write("查看书的id:" + id);
//3.取出对应的书:
Book book = DBUtils.findBookById(id);
writer.write("<br>" + book.toString());
//4.把浏览的书的ID存在cookie中,也就是通过cookie形式返回给客户端、
//4.1获取客户端的Cookies
Cookie[] cks = request.getCookies();
if(cks == null){//未传过cookie
System.out.println("未传过cookie");
//存一个Cookie
Cookie ck = new Cookie("historyBookIds",str_id);
ck.setMaxAge(60 * 60);//1小时
response.addCookie(ck);
}else{//有Cookie
System.out.println("有传过cookie");
//拼接bookid
for (Cookie ck : cks) {
if(ck.getName().equals("historyBookIds")){
String historyBookIds = ck.getValue();
System.out.println("以前访问过的书的ID:" + historyBookIds);
//判断是否有当前的ID包括在里面
if(historyBookIds.equals(str_id)) return;//如果第一个和第二个相同,则不执行后面的代码
if(historyBookIds.startsWith(str_id)){
historyBookIds = historyBookIds.replaceAll(str_id + "-", "");
}else{
historyBookIds = historyBookIds.replaceAll( "-" + str_id, "");
}
//拼接
historyBookIds += "-" + str_id;
System.out.println("现在访问过的书的ID:" + historyBookIds);
//响应客户端
ck.setValue(historyBookIds);
ck.setMaxAge(60 * 60);
response.addCookie(ck);
}
}
}
}
}
Session API
session概述
在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。
Session和Cookie的主要区别在于:
- Cookie是把用户的数据写给用户的浏览器。
- Session技术把用户的数据写到用户独占的session中。
Session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。
Session原理图
- 每个浏览器存储自己的数据到Session中
- Session的数据是不能被其它浏览器共享的
- Session一般可用于判断用户是否登录
Session小实验
package com.gyf.web.servlet.lesson04;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@WebServlet("/Lesson04Servlet1")
public class Lesson04Servlet1 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//通过request获取Session对象
HttpSession session = request.getSession();
//存数据到session
session.setAttribute("name", "gyf");
}
}
package com.gyf.web.servlet.lesson04;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@WebServlet("/Lesson04Servlet2")
public class Lesson04Servlet2 extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setHeader("content-type", "text/html;charset=utf-8");
//通过request获取Session对象
HttpSession session = request.getSession();
//获取session的值
String name = (String) session.getAttribute("name");
//响应客户端
response.getWriter().write(name);
}
}
Session的疑问
Session的实现原理:
- 当浏览器访问后台时,后台会创建一个Session对象,并分配一个ID
- 把这个Session ID通过响应头Cookie的形式返回给客户端
- 客户端就会把Cookie存在本地
- 当下次再访问这个站点时,会把sessionid通过请求头cookie的形式传回给后台
疑问:服务器是如何实现一个session为一个用户浏览器服务的?
服务器会为每个浏览器分配一个session ID,然后把Session ID通过Cookie的形式存储在客户端
Session的应用场景
购物车实现
购物车实现方案:
1.把商品存在session中
问题:
- 当浏览器重新打开,以前数据不在了【把书的id放在cookie】
- 当服务重启后,以前数据也不在【1.把书的id放在cookie,2.把session保存起来】
package com.gyf.web.servlet.lesson06;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.gyf.model.Book;
import com.gyf.web.servlet.lesson01.DBUtils;
@WebServlet("/BuyBookServlet")
public class BuyBookServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setHeader("content-type","text/html;charset=utf-8");
//用户买书-书放在购物车【掌握】
//1.获取书的ID
String bookId = request.getParameter("id");
//2.通过书id找到这本书
Book book = DBUtils.findBookById(Integer.parseInt(bookId));
//3.把这个书放在购物车List<Book> cart
//3.1先从session中取出购物车
List<Book> cart = (List<Book>) request.getSession().getAttribute("cart");
//3.2如果没有购物车对象,就创建一个
if(cart == null){
System.out.println("当前session没有购物车");
cart = new ArrayList<Book>();
}else{
System.out.println("当前session有购物车");
}
cart.add(book);
//4.把这个list放在session中
request.getSession().setAttribute("cart", cart);
//5.显示购物车的信息
PrintWriter writer = response.getWriter();
writer.write("购物车信息:");
for(Book b : cart){
writer.write("<br>" + b.getName());
}
}
}
package com.gyf.web.servlet.lesson06;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.gyf.model.Book;
import com.gyf.web.servlet.lesson01.DBUtils;
@WebServlet("/ShowAllBookServlet6")
public class ShowAllBookServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setHeader("content-type","text/html;charset=utf-8");
//1.显示书的数据给浏览器
PrintWriter writer = response.getWriter();
writer.write("购买图书列表<br>");
/*
* <a href='/项目名/BuyBookServlet?id=1'>书名</a>
* Servlet返回给客户端的数据可以是html标签数据
*/
for(Entry<Integer, Book> entry : DBUtils.getAllBooks().entrySet()){
Book book = entry.getValue();
String aStr = "<a href='http://localhost:8080" + request.getContextPath() + "/BuyBookServlet?id="+ book.getId() +"'>"+ book.getName() + "- 点击购买</a><br>";
writer.write(aStr);
}
}
}
验证码登陆
package com.gyf.web.servlet.lesson07;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.dsna.util.images.ValidateCode;
/**
* 验证码的Servlet
*/
@WebServlet("/ValidateCodeServlet")
public class ValidateCodeServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.生成验证码
ValidateCode vc = new ValidateCode(100, 30, 4, 6);
System.out.println("生成的验证码" + vc.getCode());
//2.把验证码存在session
request.getSession().setAttribute("code", vc.getCode());
vc.write(response.getOutputStream());
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/day11-20180322/LoginServlet" method="post">
<table border="1">
<tr>
<td>用户名</td>
<td><input type="text" name="username" /></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" name="password" /></td>
</tr>
<tr>
<td>验证码</td>
<td><input type="text" name="code" /> <img alt=""
src="/day11-20180322/ValidateCodeServlet"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="登陆"></td>
</tr>
</table>
</form>
</body>
</html>
package com.gyf.web.servlet.lesson07;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class LoginServlet
*/
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setHeader("content-type", "text/html;charset=utf-8");
//1.获取请求参数
String username = request.getParameter("username");
String password = request.getParameter("password");
String client_code = request.getParameter("code");
//2.获取服务端的code
String server_code = (String) request.getSession().getAttribute("code");
//3.验证码对比
if(!server_code.equalsIgnoreCase(client_code)){
response.getWriter().write("验证码不对");
}else{
response.getWriter().write("验证码正确");
}
//4.把验证码从服务器删除
//request.getSession().removeAttribute("code");
request.getSession().invalidate();//让session的所有数据都删除
System.out.println(request.getSession().getAttribute("code"));
}
}
HttpSession常用方法
把数据保存在HttpSession对象中,该对象也是一个域对象。
void setAttribute(String name,Object value);存数据
Object getAttribute(String name);取数据
void removeAttribute(String name);删除一个key数据
HttpSession.getId();获取Session的ID
setMaxInactiveInterval(int interval) 设置session的存活时间,单位是秒。
invalidate() 使此会话无效,把session数据删除
getSession():内部执行原理
HttpSession request.getSession():内部执行原理
1、获取名称为JSESSIONID的cookie的值。
2、没有这样的cookie,创建一个新的HttpSession对象,分配一个唯一的SessionID,并且向客户端写了一个名字为JSESSIONID=sessionID的cookie
3、有这样的Cookie,获取cookie的值(即HttpSession对象的值),从服务器的内存中根据ID找那个HttpSession对象:
找到了:取出继续为你服务。
找不到:从2开始。
HttpSession request.getSession(boolean create)
参数:
true:和getSession()功能一样。
false:根据客户端JSESSIONID的cookie的值,找对应的HttpSession对象,找不到返回null(不会创建新的,只是查询)。 这个在演示的时候似乎不一样
Session的状态
Session的状态三种:
创建:当浏览器第一次访问服务器动态资源就创建
活着:服务器应用运行时
死亡:
Session.invalidate();强制销毁
超时:默认30分钟
setMaxInactiveInterval(int )单位秒
在Web.xml中配置Session的有效时间
Session的持久化
为什么要持久化Session
持久化的优点:确保在服务器重启或单个Web应用重启后,能恢复重启前的会话;
Session在其生命周期中,可能会在运行时状态和持久化状态之间转换。
搁置
会话从运行时状态变为持久化状态的过程称为 —— 搁置;
在以下情况下,Session会被搁置:
当服务器重启或单个Web应用终止时,Web应用中的Session会被搁置;
会话处于不活动状态的时间太长,达到了特定的限定值;
Web应用中处于运行状态的会话数目太多,达到了特定的限制值,部分Session被搁置
激活
会话从持久化状态变为运行时状态的过程称为激活;
在以下情况下,Session会被激活:
当服务器重启或单个Web应用重启时,Web应用中的Session会被激活
处于Session中的客户端想Web应用发出HTTP请求,相应的Session会被激活
持久化对象系列化接口
当存在Session中的对象,重启tomcat时,如果有session对象,会出现下面的问题
表示对象不能被序列化,需要让Book实现序列化接口Serialize
就是说只需要实现一个Serializable接口就可以持久化Session