Cookie增删查和HttpSession增删查&自动登入案例&验证码登入与注销案例
回顾
上次课学习了两个对象:
- 响应HttpServletResponse
- 上下文对象ServletContext
获取当前工程的资源文件
ServletContext的方法 | 功能 |
---|---|
InputStream getResourceAsStream(String path) | 获取web目录下资源,转成一个输入流 |
String getRealPath(String path) | 获取真实的路径 |
String getInitParameter(String name) | 获取web.xml中全局的配置参数 <context-param> |
获取作用域对象的值
ServeltContext的方法 | 作用 |
---|---|
Object getAttribute(String name) | 获取上下文域中值 |
void setAttribute(String name, Object value) | 向上下文域中添加键和值 |
void removeAttribute(String name) | 通过键删除上下文域中键和值 |
response设置响应行的方法
状态码的方法 | 描述 |
---|---|
setStatus(int status) | 作用: 设置状态码 常见状态码: 404:找不到资源 500:服务器代码异常 304:静态资源使用了缓存 302:页面进行了跳转 405:没有重写doGet或doPost方法 200:服务器正常响应 |
response设置响应头的方法
响应头的方法 | 描述 |
---|---|
void setHeader(String name, String value) | 设置响应头的键和值 |
void setContentType(String type) | 专门设置content-type响应头 1. 告诉浏览器,服务器使用的编码 2. 设置响应的编码,相当于setCharacterEncoding() 3. 设置响应的内容类型,如:text/html |
输出响应的两种方式
响应体的方法 | 描述 |
---|---|
OutputStream getOutputStream() 处理字节 | 响应的字节输出流 |
PrintWriter getWriter() 处理字符 | 响应的字符输出流 |
学习目标
- Cookie
- 能够说出会话的概念
- 能够说出cookie的概念
- 能够创建、发送、接收、删除cookie
- 能够说出cookie执行原理
- HttpSession
- 能够说出session的概念
- 能够获取session对象、添加、删除、获取session中的数据
学习内容
1. 两种会话的技术
目标:
-
什么是会话
-
会话有哪两种技术
会话的概念
-
生活中的会话
概念:两个人一次交流的活动
- 会话的创建:有任何一方接通的电话
- 会话过程:两个人多次互相的向对话发送语音信息
- 会话结束:有任何一方挂了电话
-
BS结构的会话
概念:浏览器与服务器的多次请求和响应的过程。一个会话包含了多次请求和响应
- 会话的创建:浏览器第一次发送请求给服务器,由服务器创建会话。
- 会话的过程:浏览器多次发送请求,服务器多次做出响应
- 会话结束:浏览器关闭或服务器会话过期
-
为什么有会话技术:
因为HTTP协议(超文本传输协议,传输HTML),是一个无状态的协议,不会记录用户的状态。服务器不能通过HTTP协议来知道用户是同一个用户还是不同的用户发送的请求。通过会话技术来识别不同的用户。
-
会话的两种技术:
- 浏览器端使用的技术:Cookie
- 服务器端使用的技术:HttpSession
HttpSession需要使用到Cookie才能实现会话的跟踪技术
小结:
-
什么是会话?
一个用户的多次请求和响应的过程
-
会话有哪两种技术?
- Cookie
- HttpSession
2. 如何查看Cookie的数据
目标
如何在不同的浏览器中查看Cookie的信息
应用场景
如何实现自动登录?
把用户名和密码保存在本地,可以被浏览器读取,再发送给服务器实现自动登录。
使用Cookie技术将数据保存在本地。
Cookie就是用来保存数据,格式是:键值对,键和值都是文本内容
如何查看Cookie信息
在Chrome中
- 点地址栏的左边按钮
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ugX6zlp9-1598876513901)(assets/image-20200831091304334.png)]
-
点Cookie查看本地信息
在Firefox中
-
按F12打开开发者模式
-
点下面的存储
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0zxvoIJk-1598876513963)(assets/image-20200831091723940.png)]
-
扩展:可以安装浏览器的插件,让浏览器的功能更加强大
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ThzhnHEB-1598876513975)(assets/image-20200831091946001.png)]
小结
问:不同浏览器之间的Cookie数据能不能共享?
因为不同的浏览器之间Cookie的数据不能共享,所以在服务器看来是不同的用户。
3. Cookie的执行原理
目标
-
什么是Cookie
-
Cookie的执行原理
什么是Cookie
特点:本质上就是一个键值对,每个Cookie只能保存一个键和值
大小:最大不能超过4K,内容就是文本的内容
Cookie运行的原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dkRCT5fH-1598876514027)(assets/image-20200831094141910.png)]
小结
服务器代码要做的事情
- 创建Cookie
- 发送Cookie
- 接收Cookie
4. Cookie的创建和发送【重点】
目标
-
如何创建Cookie
-
如何将创建的Cookie写入到浏览器端
创建和发送的方法
Cookie类的方法 | 作用 |
---|---|
Cookie(String name,String value) | 指定键和值,创建一个Cookie对象,注:它没有无参的构造方法 |
HttpServletResponse对象的方法 | 作用 |
---|---|
addCookie(Cookie cookie) | 响应对象的方法,将服务器端创建好的Cookie对象发送给浏览器 |
创建Cookie的案例演示
需求:
在Servlet中创建一个Cookie(“user”,“NewBoy”),并且写到浏览器端去。
运行效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xVPZPKSN-1598876514031)(assets/image-20200831094943676.png)]
代码
package com.itheima.servlet;
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 java.io.IOException;
import java.io.PrintWriter;
/**
* 在Servlet中创建一个user=NewBoy,并且写到浏览器端去。
*/
@WebServlet("/demo1")
public class Demo1CreateCookieServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.创建一个Cookie对象
Cookie man = new Cookie("user", "NewBoy");
//2.使用响应对象发送给浏览器
response.addCookie(man);
//3.输出提示信息
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print("向浏览器发送了一个Cookie信息");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
查询响应头
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-brCtgsC9-1598876514037)(assets/image-20200831094931257.png)]
小结
-
如何创建Cookie:
new Cookie("键","值") 键和值都是String类型
-
如何写入Cookie:
response.addCookie(Cookie对象)
5. 设置Cookie过期的时间
目标
如何设置Cookie的过期时间
设置Cookie过期时间的方法
在默认的情况下,只要浏览器关闭,Cookie会失效,不会永久保存下来
Cookie的方法 | 说明 |
---|---|
void setMaxAge(int expiry) | 设置Cookie保存的时间,单位是秒。 正整数:以秒为单位设置Cookie过期的时间 零:表示删除Cookie 负整数:无效,与没有设置的效果一样,浏览器关闭就失效 |
案例:设置Cookie的过期时间
需求:
在写入Cookie之前先设置Cookie过期的时间,设置为10分钟以后过期
效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BhGnjn4E-1598876514041)(assets/image-20200831095741950.png)]
代码
package com.itheima.servlet;
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 java.io.IOException;
import java.io.PrintWriter;
/**
* 在Servlet中创建一个user=NewBoy,并且写到浏览器端去。
*/
@WebServlet("/demo1")
public class Demo1CreateCookieServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.创建一个Cookie对象
Cookie man = new Cookie("user", "NewBoy");
//1.5 在要发送给浏览器之前设置过期时间
man.setMaxAge(60 * 10); //设置为10分钟
//2.使用响应对象发送给浏览器
response.addCookie(man);
//3.输出提示信息
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print("向浏览器发送了一个Cookie信息");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
查询响应头
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KwzbBsqf-1598876514045)(assets/image-20200831095822549.png)]
小结
-
如何设置Cookie过期的时间?
setMaxAge()
-
参数是正,负,0分别表示什么意思?
- 正:以秒为单位
- 负数:无效
- 零:删除
6. 读取Cookie的信息【重点】
目标
如何从浏览器读取Cookie的信息到服务器
读取Cookie的方法
HttpServletRequest对象 | 作用 |
---|---|
Cookie[] getCookies() | 获取浏览器发送过来的所有的Cookie对象,返回Cookie数组 |
Cookie的方法 | 作用 |
---|---|
String getName() | 获取Cookie的键 |
String getValue() | 获取Cookie的值 |
演示案例
需求
- 修改写入的Servlet,创建两个Cookie
- 创建Servlet读取所有Cookie信息,显示在浏览器上。
代码
写入
package com.itheima.servlet;
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 java.io.IOException;
import java.io.PrintWriter;
/**
* 在Servlet中创建一个user=NewBoy,并且写到浏览器端去。
*/
@WebServlet("/demo1")
public class Demo1CreateCookieServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.创建一个Cookie对象
Cookie man = new Cookie("user", "NewBoy");
//1.5 在要发送给浏览器之前设置过期时间
man.setMaxAge(60 * 10); //设置为10分钟
//2.使用响应对象发送给浏览器
response.addCookie(man);
//再创建一个Cookie对象
Cookie age = new Cookie("age", "22");
age.setMaxAge(60 * 5); //设置为5分钟
response.addCookie(age);
//3.输出提示信息
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print("向浏览器发送了两个Cookie信息");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
读取
package com.itheima.servlet;
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 java.io.IOException;
import java.io.PrintWriter;
/**
* 读取所有的Cookie信息并且显示
*/
@WebServlet("/demo2")
public class Demo2ReadCookiesServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//1.通过请求对象的方法读取所有的Cookie数组
Cookie[] cookies = request.getCookies();
//2.要判断一个数组是否为空,如果浏览器端没有Cookie,会返回空
if (cookies == null) {
out.print("浏览器端没有Cookie信息");
} else {
//遍历每个Cookie对象
for (Cookie cookie : cookies) {
//获取cookie的键和值
String name = cookie.getName();
String value = cookie.getValue();
out.print("Cookie的名字:" + name + ",值:" + value + "<hr/>");
}
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
执行效果
响应头
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QJvMElZe-1598876514049)(assets/image-20200831100817748.png)]
浏览器查看请求头
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7SqlBWQk-1598876514052)(assets/image-20200831101439608.png)]
小结
HttpServletRequest对象 | 作用 |
---|---|
Cookie[ ] getCookies() | 获取浏览器端所有的Cookie对象,返回Cookie的数组 |
Cookie类的方法 | 作用 |
---|---|
String getName() | 获取Cookie的名字 |
String getValue() | 获取Cookie的值 |
7. 设置Cookie的访问路径
目标
- 如何设置Cookie的访问路径?
- 设置访问路径
设置访问路径的作用
- 在不同的路径下可以设置相同的键,避免键被覆盖
- 在访问特定路径的时候,才将Cookie的信息发送给服务器
设置路径的方法
Cookie设置路径的方法 | 功能 |
---|---|
void setPath(路径) | 只有访问这个路径或它的子路径,浏览器才会发送Cookie的信息给服务器。 如果没有设置,默认是当前项目的访问地址 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-39XMJ6Zj-1598876514053)(assets/image-20200831102133255.png)]
设置路径的案例
需求
创建一个Cookie,设置过期时间,设置Cookie的访问路径
效果
响应头
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8oQOJHxl-1598876514055)(assets/image-20200831102603105.png)]
代码
package com.itheima.servlet;
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 java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/demo3")
public class Demo3PathServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print("保存了用户名在Cookie中");
Cookie username = new Cookie("username", "Rose");
username.setMaxAge(60 * 60 * 24 * 7); //设置过期的时间是一周
//设置它的访问路径, getContextPath()当前项目的访问地址
username.setPath(request.getContextPath() + "/login.html");
response.addCookie(username);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
效果
只在访问login.html浏览器才会将Cookie的数据发送给服务器
小结
如果访问路径是setPath("/day-cookie"),以下路径浏览器会发送Cookie给服务器?
访问地址 | 会不会发送Cookie给服务器 |
---|---|
http://localhost:8080/day-cookie/ | 会,因为路径匹配 |
http://localhost:8080/day-cookie/aa/bb | 会,因为访问的是子路径 |
http://localhost:8080/day/ | 不会,路径不匹配 |
http://localhost:8080/ | 不会,访问父路径 |
8. 删除Cookie
目标
学习删除Cookie的方法
方法
Cookie的删除 | 说明 |
---|---|
setMaxAge(0) | 删除一个Cookie |
案例:删除指定的Cookie
需求
删除指定的Cookie信息,注意Cookie的访问路径要相同
代码
package com.itheima.servlet;
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 java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/demo4")
public class Demo4DeleteServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.创建一个同名的Cookie
Cookie cookie = new Cookie("user", null);
//2.设置过期的时间为0,表示删除Cookie
cookie.setMaxAge(0);
//注:访问地址也要一样
cookie.setPath(request.getContextPath()); //默认是这个
//3.发送到浏览器端
response.addCookie(cookie);
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print("Cookie被删除");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
小结
删除Cookie使用哪个方法?setMaxAge(0)
9. 创建Cookie的工具类
目标
编写一个工具类,用于读取Cookie指定键的值。如果键不存在,则返回null。如果存在就返回值
代码
工具类
package com.itheima.utils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
/**
* 工具类
* 用于读取Cookie指定键的值
* 如果键不存在,则返回null
* 如果存在就返回值
*/
public class CookieUtils {
/**
* 通过Cookie的键,获取值
* @param request 请求对象
* @param name cookie的键
* @return cookie的值,如果没有返回null
*/
public static String getCookieValue(HttpServletRequest request, String name) {
//1.通过请求对象的方法读取所有的Cookie数组
Cookie[] cookies = request.getCookies();
//2.要判断一个数组是否为空,如果浏览器端没有Cookie,会返回空
if (cookies != null) {
//遍历每个Cookie对象
for (Cookie cookie : cookies) {
//获取cookie的键和值
String key = cookie.getName();
String value = cookie.getValue();
//比较name和key是否相等
if (key.equals(name)) {
return value;
}
}
}
//没有找到返回null
return null;
}
}
工具类的使用
package com.itheima.servlet;
import com.itheima.utils.CookieUtils;
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 java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/demo5")
public class Demo5UseUtilsServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用工具类获取一个Cookie的值
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//调用工具类
String age = CookieUtils.getCookieValue(request, "age");
out.print("Cookie中age的值是:" + age);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
10. 案例part1:Cookie实现自动登录Servlet【作业】
目标
能够使用Cookie保存用户名和密码
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fIvwRd41-1598876514057)(assets/1568899204362.png)]
分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jcu6CpCX-1598876514059)(assets/image-20200831112951460.png)]
Servlet步骤
- 得到表单提交的数据:用户名和密码
- 判断用户名和密码实现登录
- 如果登录成功,则判断是否勾选了记住我。
- 如果勾选了,则将用户名和密码写入Cookie中,保存一周的时间,并跳转到成功页面。
- 否则跳转到登录失败页面
代码
LoginServlet
package com.itheima.servlet;
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 java.io.IOException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 得到用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
//2. 判断是否登录成功
if ("admin".equals(username) && "123".equals(password)) {
//3. 如果登录成功,判断是否勾了记住我
String remember = request.getParameter("remember");
//4. 如果勾了
if (remember != null) {
//创建Cookie保存用户名和密码
Cookie cname = new Cookie("username", username);
cname.setMaxAge(60 * 60 * 24 * 30); //保存1个月
cname.setPath(request.getContextPath() + "/login.html"); //设置访问路径
//5. 发送Cookie到浏览器端
response.addCookie(cname);
Cookie cpass = new Cookie("password", password);
cpass.setMaxAge(60 * 60 * 24 * 30); //保存1个月
cpass.setPath(request.getContextPath() + "/login.html"); //设置访问路径
response.addCookie(cpass);
}
//跳转到登录成功的页面
response.sendRedirect("success.html");
}
else {
//登录失败
response.sendRedirect("failure.html");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
11. 案例part2:Cookie实现自动登录浏览器端
目标
浏览器端代码的实现
JS代码
读取指定键的值commons.js
- 得到当前路径下所有的cookie信息
- 如果不为空字符串,则按分号进行拆分成数组,数组中有2个元素
- 每个元素再按等于号来拆分,0是名字,1是值
- 如果name与key相等,就返回value
- 没找到返回空字符串
页面加载完毕的代码
- 页面加载完成,调用工具类读取cookie的信息
- 如果有username不为空串,并且password也不为空串
- 则将用户名和密码赋值给文件框,并且提交表单
commons.js
/**
工具方法,通过名字得到值,如果没有这个键就返回null
*/
function getCookieValue(name) {
//这个属性可以读取浏览器端所有的cookies
let cookies = document.cookie; //username=admin; password=123 密码前面有空格
//按;和空格拆分
let strings = cookies.split("; ");
//遍历数组
for (let string of strings) { //username=admin
//按等号进行拆分
let arr = string.split("="); //arr[0]=username键 arr[1]=admin值
let key = arr[0]; //键
let value = arr[1]; //值
//如果key与name相等表示找到
if (key == name) {
return value;
}
}
//最后返回null
return null;
}
代码
login.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
<!--导入js文件-->
<script src="js/commons.js"></script>
</head>
<body>
<h2>用户登录</h2>
<form action="login" method="post" id="loginForm">
<table>
<tr>
<td width="60">用户名</td>
<td><input type="text" name="username" id="username"/></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" name="password" id="password"/></td>
</tr>
<tr>
<td>记住我</td>
<!--没有value属性的前提下,点中它的值是on,如果没有选中是没有值的 -->
<td><input type="checkbox" name="remember"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="登录"/></td>
</tr>
</table>
</form>
<script type="text/javascript">
//当前页面加载完毕,从cookie中读取用户名和密码的信息
window.onload = function () {
let username = getCookieValue("username");
let password = getCookieValue("password");
//用户名和密码都不能为空才提交数据给服务器
if (username!=null && password!=null) {
//把用户名和密码赋值给两个文本框
document.getElementById("username").value = username;
document.getElementById("password").value = password;
//提交表单
document.getElementById("loginForm").submit();
}
}
</script>
</body>
</html>
小结
实现自动登录的思路
- 在浏览器端通过JS读取本地用户名和密码
- 自动提交给服务器
- 服务器端获取用户名和密码进行判断
上午的回顾
创建Cookie对象: new Cookie("键","值")
发送给浏览器:response.addCookie(Cookie对象)
获取浏览器发送回来的cookie: Cookie[] request.getCookies()
设置Cookie的过期时间:setMaxAge(秒),如果设置为0,表示删除
设置Cookie的访问路径:setPath(/路径)
获取Cookie的名字和值:getName() getValue()
12. HttpSession对象的方法
目标
-
如何得到会话对象
-
学习HttpSession接口中常用的方法
Session存储数据的特点
在服务器端用的最多的就是会话域对象
- 在服务器的内存中保存用户的数据
- 每个用户有自己的存储空间,数据不能共享
- 每个用户有自己的ID,会话ID用来识别不同的用户
类似于超市的储物柜
Session和Cookie的主要区别
- Cookie:
- 存储在浏览器的硬盘中
- 每个Cookie只能保存一个键和值
- 键和值都是字符串类型
- Session
- 存储在服务器的内存中
- 每个Session可以保存多个键和值
- 键是字符串,值是Object类型
HttpSession接口的方法(重点)
得到会话的方法
HttpServletRequest创建会话的方法 | 描述 |
---|---|
HttpSession request.getSession() | 通过请求对象获取一个会话对象 |
创建会话的时机
用户第一次访问,并且调用上面的getSession()方法就创建一个会话对象,并且会分配会话ID给这个用户。
如果第二次以后访问,就会返回第一次创建的同一个会话。每个用户只对应一个会话对象。
org.apache.catalina.session.StandardSessionFacade@4105c137
public class StandardSessionFacade implements HttpSession
会话对象由Tomcat实现,并且由Tomcat实例化
案例:会话接口中方法的演示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FtsDZwz2-1598876514125)(assets/image-20200831145040455.png)]
步骤
- 得到会话对象
- 使用会话对象使用上面的各个方法
效果
代码
package com.itheima.servlet;
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;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Timestamp;
@WebServlet("/demo1")
public class Demo1SessionServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//创建一个会话对象
HttpSession session = request.getSession();
out.print("会话对象是:" + session + "<br/>");
//每个用户都有一个唯一的会话ID,格式是:32位的十六进制数
out.print("会话的ID是:" + session.getId() + "<hr/>");
//时间戳:java.sql.Timestamp(毫秒数) 构造方法传递的参数就是一个毫秒数
//获取会话创建的时间,返回的是一个long类型,1970年1月1日到这个时间之间相差的毫秒数
out.print("会话创建的时间:" + new Timestamp(session.getCreationTime()) + "<br/>");
//判断当前会话是否是新的会话
out.print("是否新的会话:" + session.isNew() + "<hr/>");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
小结
如何创建或获取一个会话对象? request.getSession(),第一次访问是创建会话,以后是获取会话。
HttpSession接口方法 | 作用 |
---|---|
String getId() | 获取会话的ID |
long getCreationTime() | 获取会话创建的时间,返回毫秒数 |
boolean isNew() | 判断是否是一个新的会话,第一次返回true,以后返回false |
13. 会话域的操作方法【重点】
目标
与会话域有关的操作方法
讲解
三个作用域
- 请求域:一个用户的一次请求
- 会话域:一个用户的所有请求
- 上下文域:所有用户的所有请求
请求域 < 会话域 < 上下文域
会话域用于:购物车,用户的权限验证
会话域中的方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IHmNxF7d-1598876514126)(assets/1552450034543.png)]
演示案例
需求
通过1个案例学习上面方法的使用
执行效果
- 先执行向会话域中添加值的Servlet
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B8abt2Xv-1598876514128)(assets/1552450585063.png)]
- 再执行从会话域中取值的Servlet
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q8sjqvow-1598876514130)(assets/1552450653128.png)]
- 另一个浏览器执行的效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-91xXAgIU-1598876514131)(assets/1552450684828.png)]
代码
SetServlet.java
package com.itheima.servlet;
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;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/demo2")
public class Demo2SetServlet extends HttpServlet {
//向会话域中添加键和值
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取会话对象
HttpSession session = request.getSession();
//2.调用方法向会话域中添加键和值
session.setAttribute("product", "娃娃");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print("向会话域中添加了键和值");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
GetServlet.java
package com.itheima.servlet;
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;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/demo3")
public class Demo3GetServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//1.获取会话对象,取的与demo2中是同一个会话对象
HttpSession session = request.getSession();
//2.从会话域中取出值
String product = (String) session.getAttribute("product");
//3.输出到页面
out.print("商品名字:" + product);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
小结:课堂提问
为什么另一个浏览器得不到放在会话域中的值?
关键点是他们的会话ID不同
14. 会话访问的原理【重点】
目标
- 学习Session的会话原理
- 保存会话ID到Cookie中
原理分析
查看SetServlet中响应头
会话ID通过Cookie发送给浏览器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-khsapLLN-1598876514133)(assets/image-20200831151050787.png)]
查看GetServlet的请求头
浏览器将会话ID通过Cookie发送给服务器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pHqjUcsl-1598876514134)(assets/image-20200831151134300.png)]
原理图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w4nxfjhf-1598876514136)(assets/image-20200831150901798.png)]
疑问
-
如果浏览器关闭,服务器上的会话信息是否还存在?
还在,直到服务器上会话过期,默认是30分钟。
-
浏览器关闭以后,还能不能得到之前会话域中的信息?
正常是不能的
-
有没有可能让浏览器关闭还可以再访问服务器上没有过期的信息?
可以,手动将会话ID保存,并且设置过期时间
演示案例
步骤
- 创建会话对象
- 得到会话的ID
- 创建一个Cookie,将它的名字设置为JSESSIONID,值为上面的会话ID
- 设置过期时间为30分钟
- 写入到浏览器
代码
package com.itheima.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/demo2")
public class Demo2SetServlet extends HttpServlet {
//向会话域中添加键和值
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取会话对象
HttpSession session = request.getSession();
//2.调用方法向会话域中添加键和值
session.setAttribute("product", "娃娃");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print("向会话域中添加了键和值");
//手动创建一个Cookie的数据,键是JSESSIONID,值是会话的ID,并且设置过期的时间
Cookie cookie = new Cookie("JSESSIONID", session.getId());
//设置过期的时间
cookie.setMaxAge(60 * 30);
//添加到浏览器端
response.addCookie(cookie);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-59yginKr-1598876514138)(assets/image-20200831152155424.png)]
小结
服务器与浏览器之间是如何将会话ID发送给对方的
通过Cookie发送
15. 修改会话过期时间的三种方式
目标
学习设置会话过期的三种方式
查看会话过期的时间
- 疑问:Session在服务器上默认的销毁时间是多久?如何查看?
修改会话过期时间的三种方式
方式一:通过代码
session.setMaxInactiveInterval(秒);
示例:设置会话过期的时间为10秒,输出过期的时间,输出会话ID
代码
package com.itheima.servlet;
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;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 获取会话过期的时间间隔
*/
@WebServlet("/demo4")
public class Demo4IntervalServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//1.获取会话对象
HttpSession session = request.getSession();
//设置过期的时间是10秒
//session.setMaxInactiveInterval(10);
//2.获取会话过期的时间间隔,单位是秒。非活动:没有新的请求
//如果设置为10秒,第9秒的时候有一次请求,服务器就会重新计时10秒
int interval = session.getMaxInactiveInterval();
out.print("会话过期的时间是:" + interval + "<hr/>");
out.print("会话ID是:" + session.getId() + "<br/>");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
方式二:可以通过web.xml的配置来实现
<!--会话的配置-->
<session-config>
<!--过期的时间: 单位是分-->
<session-timeout>5</session-timeout>
</session-config>
疑问:如果同时使用代码和配置的方式以哪个为准?
以代码为准,后面的代码会覆盖前面的配置
方式三:立刻失效
应用场景:用于用户退出或注销,让会话马上过期。会释放当前用户会话域中所有资源
session.invalidate();
小结
修改会话过期的时间有哪三种方式?
-
session.setMaxInactiveInterval(秒)
-
在web.xml中配置
<session-config> <session-timeout>分钟</session-timeout> </session-config>
-
马上失效:session.invalidate()
16. URL重写:禁用Cookie的处理【了解】
目标
- Cookie禁用后重写向的处理
- Cookie禁用后超连接的处理
禁用Cookie后会话中共享数据导致的问题
- 把浏览器的Cookie禁用
- 在一个Servlet1中向session中添加一个数据,重定向到Servlet2。
- 在另一个Servlet2中的session中读取数据并显示出来
- 无法得到原来会话中的信息
效果
浏览器不会把会话ID再发送给服务器,导致服务器上会话数据读取不出来。
解决方法
不能通过Cookie来发送会话ID,在地址栏上发送会话id
- 分隔符是;
- jsessionid是小写
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hWCcSKPM-1598876514141)(assets/image-20200831155832231.png)]
重定向的解决方法
什么是URL重写:
响应对象的方法 | 说明 |
---|---|
response.encodeRedirectURL(path) | 在要跳转过去的地址栏后面加上会话id |
重写前的URL:
demo3
重写后的URL:
demo3;jsessionid=5933A9F542C0938DDE196E3DF80C0C82
代码
//URL重写:在地址后面加上会话的ID,使用分号隔开
String url = response.encodeRedirectURL("demo3");
//输出到浏览器上
out.println("<a href='" + url + "'>跳转到demo3</a>");
思考
使用URL重写以后,不同的浏览器可不可以访问其它浏览器的会话数据?
理论上是可以访问的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OkrSf0Lo-1598876514145)(assets/image-20200831160506165.png)]
小结
方法 | 说明 |
---|---|
response.encodeRedirectURL(path) | 将会话ID添加到URL的后面,使用分号隔开 |
17. 案例part1:登录验证码的准备【作业】
目标
登录验证码环境的搭建
流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2OGzsFOe-1598876514148)(assets/image-20200831161318298.png)]
需求:用户登录的时候使用验证码进行验证
登录成功后将用户信息保存到会话域中,并且跳转到WelcomeServlet,然后在WeclcomeServlet中读取用户信息,显示欢迎信息。在WelcomeServlet上显示退出的链接,点退出,注销会话信息。
步骤
- 复制登录的原型login.html,添加name属性
- 复制昨天已经写好的验证码,修改验证码代码。将字符串保存在会话域中
- 搭建整个项目,今天不访问数据库。
登录页面
<div class="col-sm-3">
<!--指定为servlet,鼠标移上去变成手-->
<img src="code" style="cursor: pointer" title="看不清换一张" id="picCode"/>
<script type="text/javascript">
//编写图片的点击事件
document.getElementById("picCode").onclick = function () {
//修改图片的src,并且指定一个变化的参数
this.src = "code?n=" + new Date().getTime();
};
</script>
</div>
验证码的Servlet
package com.itheima.servlet;
import javax.imageio.ImageIO;
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;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
@WebServlet("/code")
public class PicCodeServlet extends HttpServlet {
//随机类
private Random random = new Random();
/**
* 随机获取一种颜色
*/
private Color getRandomColor() {
//随机得到r,g,b的取值,范围是0~255
int r = random.nextInt(256);
int g = random.nextInt(256);
int b = random.nextInt(256);
return new Color(r, g, b); //red红 green绿 blue蓝
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置MIME类型
response.setContentType("image/jpeg");
//定义宽和高的值
int width = 90;
int height = 30;
//1. 创建一张图片,参数:宽,高,图片模式
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//获取画笔对象
Graphics graphics = image.getGraphics();
//整个图片填充白色
graphics.setColor(Color.WHITE);
graphics.fillRect(0,0,width,height);
//2. 随机绘制4个验证码
char[] arr = { 'A', 'B', 'C', 'D', 'N', 'E', 'W', 'b', 'o', 'y', '1', '2', '3', '4','5','6','7','8' };
//设置字体,字体对象有三个参数:字体名字,字体样式(加粗,斜体), 大小
graphics.setFont(new Font(Font.DIALOG_INPUT, Font.BOLD + Font.ITALIC, 19));
//创建一个StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 4; i++) {
//随机获取1个索引号
int index = random.nextInt(arr.length);
//随机获取字符数组的一个字符
char c = arr[index];
sb.append(c);
//每个字符的颜色不同
graphics.setColor(getRandomColor());
//写字符,参数:文字内容,x,y坐标 (把字符转成字符串)
graphics.drawString(String.valueOf(c),10+(i*20), 20);
}
//把验证码放在会话域中
HttpSession session = request.getSession();
//转成字符串以后放到会话域中
session.setAttribute("code", sb.toString());
System.out.println("生成的验证码是:" + sb);
//3. 绘制8条干扰线
for (int i = 0; i < 8; i++) {
//指定颜色
graphics.setColor(getRandomColor());
int x1 = random.nextInt(width);
int y1 = random.nextInt(height);
int x2 = random.nextInt(width);
int y2 = random.nextInt(height);
//画线,起点和终点
graphics.drawLine(x1,y1,x2,y2);
}
//4. 把图片输出到浏览器,参数:输出的图片对象,图片格式,响应输出流
ImageIO.write(image,"jpg", response.getOutputStream());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
18. 案例part2:登录的Servlet
目标
实现登录的Servlet
步骤
- 使用昨天的代码实现验证码的绘制
- 将随机产生的字符串放在会话域中
- 用户登录的时候提交验证码的字符串
- 比较表单提交的字符串是否与会话域中的字符串相等,如果相等则验证成功
- 登录一次以后删除会话域中的验证码字符串
- 登录成功以后保存用户的信息到会话域中,并且跳转到WelcomeServlet
代码
package com.itheima.servlet;
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;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//1.判断验证码,验证码放在会话域中
HttpSession session = request.getSession(); //获取会话对象
String code = (String) session.getAttribute("code"); //从会话域中取出验证码
//获取用户提交的验证码
String vcode = request.getParameter("vcode");
//判断验证码是否正确,忽略大小写比较
if (vcode.equalsIgnoreCase(code)) {
//2.判断用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("admin".equals(username) && "123".equals(password)) {
//把用户的信息保存在会话域中,这是一个标记,表示用户已经登录成功
session.setAttribute("username", username);
//3.如果成功跳转到welcome
response.sendRedirect("welcome");
}
//用户名密码错误
else {
out.print("<script>");
out.print("alert('用户名或密码错误');");
out.print("location.href='login.html';");
out.print("</script>");
}
}
//验证码错误
else {
//使用脚本跳转,可以看到错误信息
out.print("<script>");
out.print("alert('验证码错误');");
out.print("location.href='login.html';");
out.print("</script>");
//4.如果失败跳转到login.html
//response.sendRedirect("login.html");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
19. 案例part3:欢迎Servlet和注销的Servlet
目标
- 在欢迎的Servlet上显示用户的信息
- 点退出链接,用户注销
- 如果没有正确登录的用户,不能访问WelcomeServlet
步骤
- 从会话域中取出用户信息并且显示
- 判断用户是否正常登录,如果是非法用户则跳转到登录页面
- 在页面上输出一个注销的连接,点注销跳转到LogoutServlet
- 注销:调用invalidate(),重定向到login.html
代码
WelcomeServlet
package com.itheima.servlet;
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;
import java.io.IOException;
import java.io.PrintWriter;
/*
这个页面必须进行权限验证
原理:只要登录成功的用户,会话域中是有数据的,没有登录的用户,会话域中用户信息是空的
*/
@WebServlet("/welcome")
public class WelcomeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//获取会话域
HttpSession session = request.getSession();
//从会话域中取出用户的信息
String username = (String) session.getAttribute("username");
//表示没有登录
if (username == null) {
//getRemoteAddr() 获取客户端的IP地址
System.out.println("拦截到了非法的用户:" + request.getRemoteAddr());
response.sendRedirect("login.html");
//后面的代码还会继续运行的
return;
}
//输出用户的信息
out.print("<h1>登录成功,欢迎您!" + username + "</h1>");
out.print("<a href='logout'>退出</a>");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
LogoutServlet
package com.itheima.servlet;
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 java.io.IOException;
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.销毁会话
request.getSession().invalidate();
//2.重定向到登录页面
response.sendRedirect("login.html");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
小结
- 验证码放在会话域中
- 登录的时候先从会话域中取出验证码,再判断用户名和密码
- 如果登录成功跳转到欢迎页面,并从会话域中取出用户信息显示
- 登录失败跳转到登录页面
- 点退出注销会话,跳转到登录页面
学习总结
-
能够说出会话的概念
创建:浏览器第一次访问,由服务器创建会话。
过程:浏览器与服务器之间的多次请求和响应
结束:浏览器关闭或服务器会话过期
-
能够说出cookie的概念
-
保存在: 浏览器端的硬盘中
-
键和值都是: 字符串类型,每个Cookie保存一个键值对
-
默认浏览器关闭: Cookie失效
-
-
能够创建、发送、接收、删除cookie
方法名 作用 new Cookie(名字,值) 创建一个Cookie对象 response.addCookie(Cookie cookie) 把Cookie发送到浏览器写入 Cookie[] request.getCookies() 获取浏览器端所有的Cookie对象数组 setMaxAge(0) 删除Cookie -
能够说出cookie执行原理
-
能够说出session的概念
-
保存在: 服务器的内存中
-
键和值:键是字符串类型,值是Object类型
-
默认过期:30分钟
修改过期的时间:
- setMaxInactiveInterval(秒)
- web.xml中配置session-config session-timeout 分钟
- invalidate() 马上过期
-
-
能够获取session对象、添加、删除、获取session中的数据
方法名 作用 request.getSession() 第一次访问创建会话,以后是获取会话对象 session.setAttribute(“键”, 值) 向会话域中添加键和值 session.getAttribute(“键”) 从会话域中取出值 session.removeAttribute(“键”) 删除键和值