JavaWeb的技术体系
Cookie与Session作用:用于浏览器与服务器之间的会话控制
关于会话的基本概念:
- 会话:从浏览器开启到浏览器关闭;
- 会话技术:用来保存在会话期间 浏览器和服务器所产生的数据;
- 会话技术分类:
- 客户端的会话技术:把会话数据保存在客户端 【cookie】
- 服务端的会话技术:把会话数据保存在服务端 【session】
一.Cookie
1.Cookie概述
1.Cookie的起源:
1)现实存在的问题
由于HTTP是无状态协议,服务器不能记录浏览器的访问状态,也就是说服务器不能区分两次请求是否由一个客户端发出。
这样的设计严重阻碍的Web程序的设计。如:在我们进行网购时,买了一条裤子,又买了一个手机。由于http协议是无状态的,如果不通过其他手段,服务器是不能知道用户到底买了什么。而Cookie就是解决方案之一。
2)问题的解决方案:Cookie
Cookie实际上就是服务器保存在浏览器上的一段信息。
浏览器有了Cookie之后,每次向服务器发送请求时都会同时将该信息发送给服务器,服务器收到请求后,就可以根据该信息处理请求。
2.Cookie的用途
- 用户登录状态的保持
- 广告推荐
3.Cookie的运行原理:
- 1.第一次向服务器发送请求时在服务器端创建一个Cookie对象
- 2.将Cookie对象发送给浏览器
- 3.以后浏览器再发请求就会携带着该Coolie对象
- 4.服务器通过不同的Cookie来区别不同的用户
4.Cookie的限制性
① Cookie作为请求或响应报文发送,无形中增加了网络流量。
② Cookie是明文传送的安全性差。
③ 各个浏览器对Cookie有限制,使用上有局限
④ Cookie的值只能是String类型,不能保存对象
2 cookie的基本使用
2.1 cookie常用的方法介绍
API介绍
Cookie:
方法 | 返回值 | 描述 |
---|---|---|
Cookie(String name, String value) | Cookie | 构造方法,创建cookie对象 |
getName() | String | 获取cookie的名称 |
getValue() | String | 获取cookie的值 |
setPath(String uri) | void | 设置cookie的路径——浏览器根据这个路径判断哪些cookie要发送给服务器 |
HttpServletResponse:
方法 | 返回值 | 描述 |
---|---|---|
addCookie(Cookie cookie) | void | 将cookie发送给浏览器 |
HttpServletRequest:
方法 | 返回值 | 描述 |
---|---|---|
getCookies() | Cookie[] | 获取浏览器发送的cookie |
演示代码
CookieServlet1:
@WebServlet(name = "CookieServlet1",urlPatterns = "/cookie1")
public class CookieServlet1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//创建cookie
Cookie cookie = new Cookie("username","tom");
//设置cookie的路径——浏览器根据这个路径判断那些cookie要发送给服务器
cookie.setPath("/day04");
//将cookie发送给浏览器
response.addCookie(cookie);
}
}
CookieServlet2:
@WebServlet(name = "CookieServlet2",urlPatterns = "/cookie2")
public class CookieServlet2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取浏览器发送的cookie
Cookie[] cookies = request.getCookies();
if(cookies != null){
for (Cookie coo : cookies) {
if("username".equals(coo.getName())){
//打印cookie的名称和值
System.out.println(coo.getName()+":"+coo.getValue());
}
}
}
}
}
cookie技术原理分析
2.2 删除cookie
API介绍
方法 | 返回值 | 描述 |
---|---|---|
setMaxAge(int expiry) | void | 设置cookie的最大生存时间(单位:秒) |
setPath(String uri) | void | 设置cookie的路径——浏览器根据这个路径判断那些cookie要发送给服务器 |
注意事项
- cookie的生存时间单位为:秒
- 要删除已经存在的cookie,后一个用来覆盖cookie必须名称与路径与原来的cookie一致
演示代码
@WebServlet(name = "MaxAgeServlet",urlPatterns = "/maxage")
public class MaxAgeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//创建cookie
/* Cookie cookie = new Cookie("name","tom");
//设置cookie的生存时间
cookie.setMaxAge(60*60*24*7);
//设置cookie的路径
cookie.setPath("/day04");
//发送cookie给浏览器
response.addCookie(cookie);*/
//=====如果需要立刻删除cookie======
//创建空数据的cookie,名称保持一致
Cookie cookie = new Cookie("name","");
//设置cookie的生存时间为0立刻死亡
cookie.setMaxAge(0);
//设置cookie的路径,与原来的cookie路径保持一致
cookie.setPath("/day04");
//发送cookie给浏览器
response.addCookie(cookie);
}
}
2.3 案例:记录用户上一次访问时间
案例需求
展示用户上一次访问服务器的时间
案例分析
实现代码
@WebServlet(name = "TimeServlet",urlPatterns = "/time")
public class TimeServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//第一次访问操作
// 1 获取用户当前访问的时间
Date date = new Date();
//格式化时间数据(注意:在cookie值中不能使用分号(;)、逗号(,)、等号(=)以及空格,否则会出现异常)
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd/hh:mm:ss");
String format1 = format.format(date);
System.out.println(format1);
// 2 将数据存入cookie
Cookie cookie = new Cookie("time", format1);
cookie.setMaxAge(60*60*24);
// 3 将数据发送给浏览器
response.addCookie(cookie);
//第二次访问操作
//获取cookie数组
Cookie[] cookies = request.getCookies();
if(cookies != null) {
for (Cookie cookie2 : cookies) {
//选择获取名称为time的cookie
if("time".equals(cookie2.getName())) {
//将数据发送页面
response.getWriter().write(cookie2.getValue());
}
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
二.Session
1.为什么需要Session
-
使用Cookie有一个非常大的局限,就是如果Cookie很多,则无形的增加了客户端与服务端的数据传输量。而且由于浏览器对Cookie数量的限制,注定我们不能再Cookie中保存过多的信息,于是Session出现。
-
Session的作用就是在服务器端保存一些用户的数据,然后传递给用户一个名字为JSESSIONID的Cookie,这个JESSIONID对应这个服务器中的一个Session对象,通过它就可以获取到保存用户信息的Session。
2.Session概述:
Session技术就好比医院发放给病人的就医卡和医院为每个病人保留病例档案的过程。当浏览器访问Web服务器时,Servlet容器就会创建一个Session对象和ID属性,其中,Session对象就相当于病历档案,ID就相当于就诊卡号。当客户端后续访问服务器时,只要将标识号传递给服务器,服务器就能判断出该请求是哪个客户端发送的,从而选择与之对应的Session对象为其服务。
需要注意的是,由于客户端需要接收、记录和回送Session对象的ID,因此,通常情况下,Session是借助Cookie技术来传递ID属性的。
3.Session的运行原理:
- 1.第一次向服务器发送请求时在服务器端创建一个Session对象,该对象有一个全球唯一的ID
- 2.在创建Session对象的同时会创建一个特殊的Cookie对象,该Cookie对象的名字是一个固定值:JSESSIONID,该Cookie对象的值就是Session对象的ID值,并且将该Cookie对象发送给浏览器
- 3.以后浏览器再发送请求就会携带着这个特殊的Cookie对象
- 4.服务器获取Cookie对象的值之后寻找与之对应的Session对象,以此来区分不同的用户
2. session的与cookie的区别
cookie | session | |
---|---|---|
会话数据保存的位置 | 浏览器 | 服务器 |
数据的安全性 | 不安全 | 安全 |
存储数是否有限制 | 有 | 无 |
3. session的应用场景
- 保存购物车数据
- 保存用户浏览器记录数据
- 保存用户登录信息数据
- 保存验证码
4. session作为域对象使用
4.1 session存储数据与获取数据
API介绍
HttpServletRequest:
方法 | 返回值 | 描述 |
---|---|---|
getSession() | HttpSession | 获取session对象 |
HttpSession:
方法 | 返回值 | 描述 |
---|---|---|
setAttribute(String name, Object value) | void | 在session中保存数据 |
etAttribute(String name) | Object | 从session中获取数据 |
removeAttribute(String name) | void | 从session中移除数据 |
注意事项
getAttribute方法返回值是Object,因此需要将返回值强制转换才能方便使用。
演示代码
SessionServlet1:
@WebServlet(name = "SessionServlet1",urlPatterns = "/session1")
public class SessionServlet1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取session对象
HttpSession session = request.getSession();
//添加数据
session.setAttribute("addr","广州");
}
}
SessionServlet2:
@WebServlet(name = "SessionServlet2",urlPatterns = "/session2")
public class SessionServlet2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取session对象
HttpSession session = request.getSession();
//获取数据
String addr = (String) session.getAttribute("addr");
System.out.println(addr);
//移除数据
session.removeAttribute("addr");
//再次获取,查看效果
String addr1 = (String) session.getAttribute("addr");
System.out.println(addr1);
}
}
4.2 session的原理分析
4.3 session实现关闭浏览器继续可以访问数据
通过上面的原理图,我们发现记录sessionid的cookie在关闭浏览器之后就到期了,那么如果我们希望——关闭浏览器继续可以访问session数据,该怎么做呢?
案例需求
关闭浏览器继续可以访问session数据
实现:访问一次服务器,然后关闭浏览器,再访问一次:两次session对象一致
案例分析
-
创建一个servlet
-
通过request对象获取session的id
-
自定义一个cookie对象,保存session的id,注意cookie名称为"JSESSIONID",设置一个比较长的生存时间
-
发送给浏览器
代码实现
@WebServlet(name = "SessionServlet3",urlPatterns = "/session3")
public class SessionServlet3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取session对象
HttpSession session = request.getSession();
System.out.println(session);
//自己创建一个cookie,要求被浏览器持久化保存起来(setMaxAge(10000))放便后期使用
Cookie cookie = new Cookie("JSESSIONID", session.getId());
//活的久一点
cookie.setMaxAge(10000);
response.addCookie(cookie);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
5. 案例:使用session存储验证码完成登录功能
5.1 案例需求
- 在登录页面用户登录的时候要查看到验证码,如图所示:
-
在生成页面验证码图片的同时,使用session存储验证码
-
在处理用户登录请求的时候,首先校验验证码
-
校验通过才能执行登录操作
5.2 案例分析
5.3 代码实现
1.页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>login</title>
<script type="text/javascript">
function changeCode(){
document.getElementById("img").src = "/day04/checkcode?r="+new Date().getTime();
}
</script>
</head>
<body>
<form action="/day04/login" method="post">
<table>
<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"></td></tr>
<!-- 通过向服务器发送请求,从服务器获取验证码数据 -->
<tr><td></td><td><img id="img" src="/day04/checkcode" onclick="changeCode();"/><a href="javascript:;" onclick="changeCode();">换一换</a><span><% if(request.getAttribute("msg")!=null){out.write(request.getAttribute("msg").toString());}%></span></td></tr>
<tr><td></td><td><input type="submit" value="登陆"></td></tr>
</table>
</form>
</body>
</html>
2.配置验证码servlet
@WebServlet(name = "CheckcodeServlet",urlPatterns = "/checkcode")
public class CheckcodeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 创建画布
int width = 120;
int height = 40;
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 获得画笔
Graphics g = bufferedImage.getGraphics();
// 填充背景颜色
g.setColor(Color.white);
g.fillRect(0, 0, width, height);
// 绘制边框
g.setColor(Color.red);
g.drawRect(0, 0, width - 1, height - 1);
// 生成随机字符
// 准备数据
String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
// 准备随机对象
Random r = new Random();
// 声明一个变量 保存验证码
String code = "";
// 书写4个随机字符
for (int i = 0; i < 4; i++) {
// 设置字体
g.setFont(new Font("宋体", Font.BOLD, 28));
// 设置随机颜色
g.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
String str = data.charAt(r.nextInt(data.length())) + "";
g.drawString(str, 10 + i * 28, 30);
// 将新的字符 保存到验证码中
code = code + str;
}
// 绘制干扰线
for (int i = 0; i < 6; i++) {
// 设置随机颜色
g.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g.drawLine(r.nextInt(width), r.nextInt(height), r.nextInt(width), r.nextInt(height));
}
// 将验证码 打印到控制台
System.out.println(code);
// 将验证码放到session中
request.getSession().setAttribute("code_session", code);
// 将画布显示在浏览器中
ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
3.登录servlet
@WebServlet(name = "LoginServlet",urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//用户请求中的验证码获取
String code = request.getParameter("code");
//获取session中保存的验证码
String code_session = (String)request.getSession().getAttribute("code_session");
//与session中保存的验证码进行校验
if(!code_session.equalsIgnoreCase(code)){
//验证码错误,告诉用户,页面提示
request.setAttribute("msg","验证码错误");
request.getRequestDispatcher("/login.jsp").forward(request,response);
return;
}
//验证码正确,登录逻辑执行
//获取用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
//调用Service方法,登录用户
UserDao userDao = new UserDao();
User loginUser = userDao.login(username,password);
if(loginUser == null){
request.setAttribute("msg","用户名或则密码错误");
request.getRequestDispatcher("/login.jsp").forward(request,response);
return;
}else{
//登陆成功,跳转主页
response.sendRedirect(request.getContextPath());
return;
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
4.service:
public class UserService {
private UserDao userDao = new UserDao();
public User login(String username, String password) {
return userDao.login(username, password);
}
}
5.dao:
public class UserDao {
private QueryRunner queryRunner = queryRunner = new QueryRunner(JdbcUtils.getDataSource());
/**
* 查询用户名和密码是否匹配的方法
*/
public User login(String username, String password) {
try {
String sql = "select * from user where username = ? and password = ?";
//查询并封装
User user = queryRunner.query(sql, new BeanHandler<User>(User.class), username, password);
return user;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
}
相关jar包、jdbc工具类、c3p0配置文件、User类、数据库user表,请自行编写
6. 案例:模拟简易session购物车
6.1 案例需求
完成session购物车案例
-
保证中文的正常显示
-
购物车数据通过Session保存,窗口关闭以后数据消失。
6.2 案例分析
-
在第一个购物界面选择商品添加到购物车,手动回退到购物界面
-
在购物车选好物品后,可以选择“买点别的”,跳转到第二个购物页面选购
-
在第二个购物界面选择商品添加到购物车,手动回退到购物界面
-
查看购物车页面
6.3 代码实现
- shopping cart1.jsp:购物车一页面球类
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>购物球类</title>
</head>
<body>
<h4>各种球大甩卖,一律八块</h4>
<form action="/day04/data" method="post" name="Ball" id="ball">
<input type="checkbox" name="data" value="篮球">篮球<br>
<input type="checkbox" name="data" value="足球">足球<br>
<input type="checkbox" name="data" value="排球">排球<br><br>
<input type="submit" value="提交">
<input type="reset" value="全部重写"><br><br>
<a href="ShoppingCart2.jsp">买点别的</a> <a href="display.jsp">查看购物车</a>
</form>
</body>
</html>
- shopping cart2.jsp:购物车二页面肉类
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>肉类购物车</title>
</head>
<body>
<h4>各种肉大甩卖,一律十块:</h4>
<form name="Meet" id="Meet" action="/day04/data" method="post">
<input type="checkbox" name="data" value="猪肉">猪肉<br>
<input type="checkbox" name="data" value="牛肉">牛肉<br>
<input type="checkbox" name="data" value="羊肉">羊肉<br><br>
<input type="submit" value="提交">
<input type="reset" value="全部重写"><br><br>
<a href="ShoppingCart1.jsp">买点别的</a> <a href="display.jsp">查看购物车</a>
</form>
</body>
</html>
- DataServlet:数据处理
@WebServlet(name = "DataServlet",urlPatterns = "/data")
public class DataServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String goods[] = request.getParameterValues("data");
HttpSession session = request.getSession();
//获取表单传过来的数据存入字符串数组中
Set<String> now= (Set <String>)session.getAttribute("list");
//当前session内的东西
//新建集合将数据存入
Set <String> set = new HashSet<String>();
if(goods!=null){
//将货物添加到集合中
Collections.addAll(set,goods);
}
if(now!=null){
//将session内当前的东西添加到集合中
set.addAll(now);
}
//将集合存入session中,命名为list
session.setAttribute("list",set);
//浏览器输出添加成功
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("<h1>添加到购物车成功</h1>");
}
}
- display.jsp:显示数据页面
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*" %>
<html>
<head>
<title>显示购物车内容</title>
</head>
<body>
<h3>你选择的结果是:</h3>
<%
Set <String> goods = (Set<String>)session.getAttribute("list");
if (goods!= null){
for (String good : goods){
out.print(good + "<br/>");
}
}
%>
</body>
</html>
7.Session的钝化与活化
1.钝化
- 关闭服务器时,服务器中的session会被序列化,并保存到本地
- Session对象中的成员需要实现序列化接口
2.活化
- 服务器启动时,会将服务器本地的Session序列化文件加载到服务器中,之前的session还可以用