目录
序
众所周知,Http协议是无状态的,也就意味着,针对浏览器与服务器之间的请求和响应(也叫会话),当两者之间的会话结束时,服务器端并不会记忆客户端(浏览器)曾经访问过。
但是,在实际应用程序开发中,有些业务需要浏览器和服务器之间能够保持会话。比如常见的登录业务,在同一个浏览器下,当用户第一次登录成功并进入首页时,下次再使用同一个浏览器访问首页时,则不需要再登录。而要实现下次访问不再登录时,需要让服务端能够识别曾经访问过它的浏览器,这就需要会话跟踪技术来实现。分别是cookie
和session
。
第一章:Cookie
1.1-Cookie概述
Cookie,有时也用其复数形式 Cookies。类型为“小型文本文件”,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息 。
总而言之,cookie就是客户端会话技术,可以将数据以存储在客户端。
1.2-Java操作Cookie
创建cookie
在服务端创建Cookie。
创建方式:Cookie 变量名 = new Cookie(name,value);
- name,cookie的名称。
- value,cookie的值。
读取cookie中的name和value
- cookie对象.getName();
- cookie对象.value();
保存cookie到客户端
保存方式:response对象.addCookie(Cookie cookie)
;
获取cookie
服务端接收客户端发送过来的cookie,通过request对象获取。
方法:Cookie[] request.getCookies()
,返回一个Cookie数组。
设置cookie的生命周期
在服务端中可以指定cookie在客户端存活的时长。
cookie对象中提供了cookie的存活时长相关方法。
方法:cookie对象.setMaxAge(int seconds)
;
- seconds,单位是秒。
- 正数:将Cookie数据写到硬盘的文件中。持久化存储。并指定cookie存活时间,时间到后,cookie文件自动失效
- 负数:默认值,关闭浏览器时,会自动失效。
- 零:0,表示删除cookie。
cookie共享问题
情况1:假设在一个tomcat服务器中,部署了多个web项目,那么在这些web项目中cookie能不能共享?
- 默认情况下cookie不能共享。
- 可以通过cookie对象的
setPath(String path)
方法:设置cookie的获取范围。默认情况下,设置的时当前的虚拟目录。- 如果要共享,则可以将path设置为
"/"
- 如果要共享,则可以将path设置为
情况2:不同的tomcat服务器间cookie共享问题?
- 域名:
- 一级域名:如:baidu.com
- 二级域名:如:news.baidu.com、tieba.baidu.com
- 一般会部署在不同的服务器上。
- 方法:
setDomain(String path)
- 如果设置一级域名相同,那么多个服务器之间cookie可以共享
- 如:
setDomain(".baidu.com")
,那么tieba.baidu.com和news.baidu.com中cookie可以共享
- 如:
- 如果设置一级域名相同,那么多个服务器之间cookie可以共享
代码
Base01.Java保存cookie到客户端
package cn.leilei.base; 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("/base01") public class Base01 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 创建cookie对象 Cookie cookie = new Cookie("userName","张三"); // 设置cookie的时间 cookie.setMaxAge(60*3); // 设置cookie路径 cookie.setPath("/"); Cookie cookie1 = new Cookie("id","10010"); // 设置cookie的时长 cookie1.setMaxAge(60*3); // 发送cookie对象给客户端 response.addCookie(cookie); response.addCookie(cookie1); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
Base02.java服务端读取客户端发送的cookie
package cn.leilei.base; 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("/base02") public class Base02 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 读取cookie Cookie[]cookies = request.getCookies(); // 循环遍历读取 for (Cookie cookie : cookies) { // 获取cookie的名称 String name = cookie.getName(); // 获取cookie的值 String value = cookie.getValue(); System.out.println(name + ":" + value); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
1.3-Cookie的原理
基于响应头set-cookie和请求头cookie实现
1.4-Cookie的特点
- cookie存储数据在客户端浏览器。
- 浏览器对于单个cookie 的大小有限制(4kb) 以及 对同一个域名下的总cookie数量也有限制(20个)
1.5-Cookie的应用场景
- cookie一般用于存出少量的不太敏感的数据。
- 常见使用场景
- 判断用户是否登陆过网站,以便下次登录时能够实现自动登录(或者记住密码)。如果我们删除cookie,则每次登录必须从新填写登录的相关信息。
- 保存上次登录的时间等信息
- 保存上次查看的页面
- 浏览计数
1.6-Cookie的缺点
(1)可能被删除,被禁用;
(2)安全性不高,纯文本形式存储,如存储密码则需加密处理;
(3)大小受限,容量4kb,相当于4000个英文字母;
(4)不同浏览器不相通,不同域中的cookie不共享;
(5)每次都需传递cookie给服务器,浪费带宽;
(6)cookie数据有路径(path)的概念,可以限制cookie只属于某个路径下。
1.7-案例
需求
打开页面,若不是第一次,则显示访问的次数及上次访问时间。
第一次访问。
下一次访问。
代码
Cookie操作的封装代码
package cn.leilei.utils; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; public class CookieUtils { // 添加Cookie public static Cookie addCookie(String name,String value){ Cookie cookie = null; try { value = URLEncoder.encode(value,"utf-8"); cookie = new Cookie(name,value); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return cookie; } // 根据名字读取对应的cookie值 public static String getValue(HttpServletRequest request, String name){ Cookie[]cookies = request.getCookies(); String value = null; if(cookies!=null){ for (Cookie cookie : cookies) { if(cookie.getName().equals(name)){ value = cookie.getValue(); try { value = URLDecoder.decode(value,"utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } break; } } } return value; } }
显示页面的jsp代码
<%@ page import="java.util.Date" %> <%@ page import="java.text.SimpleDateFormat" %> <%@ page import="cn.leilei.utils.CookieUtils" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> <style> body{ background: #000; } h1{ color:gold; } h2{ color: white; } </style> </head> <body> <% // 检测是否有lastTime对应的cookie值 String value = CookieUtils.getValue(request, "lastTime"); if (value == null) { // 说明第一次,访问该页面 out.write("<h1>欢迎光临,您是第1次访问。</h1>"); setInfo(response, "1"); } else { // 说明之前访问过 // 获取之前的访问时间 String time = CookieUtils.getValue(request, "lastTime"); // 获取之前的访问次数 String count = CookieUtils.getValue(request, "count"); count = (Integer.parseInt(count) + 1) + ""; // 显示消息 out.write("<h1>欢迎光临,您是第" + count + "次访问。</h1>"); out.write("<h2>上次访问时间是:" + time); // 重新设置cookie setInfo(response, count); } %> <%--封装设置cookie信息方法--%> <%! // 封装方法 private void setInfo(HttpServletResponse response, String count) { Date date = new Date(); SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss"); String v = format.format(date); Cookie cookie1 = CookieUtils.addCookie("lastTime", v); cookie1.setMaxAge(60 * 60 * 24 * 30); response.addCookie(cookie1); Cookie cookie2 = CookieUtils.addCookie("count", count); cookie2.setMaxAge(60 * 60 * 24 * 30); response.addCookie(cookie2); } %> </body> </html>
第二章:Session
2.1-Session概述
Session:在计算机中,尤其是在网络应用中,称为“会话控制”。Session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的Web页之间跳转时,存储在Session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web页时,如果该用户还没有会话,则Web服务器将自动创建一个 Session对象。当会话过期或被放弃后,服务器将终止该会话。Session 对象最常见的一个用法就是存储用户的首选项。例如,如果用户指明不喜欢查看图形,就可以将该信息存储在Session对象中。有关使用Session 对象的详细信息,注意会话状态仅在支持cookie的浏览器中保留。
2.2-Java操作Session
获取Session对象
可以通过request对象获取Session对象
获取方式: HttpSession session = request.getSession()
;
使用Session对象
- 设置
- 方法:
void setAttribute(String name, Object value)
- 参数:
- name,名称
- value,值,可以是任意类型的值。
- 方法:
- 获取
- 方法:
Object getAttribute(String name)
- 参数:
- name,名称
- 方法:
- 移除
- 方法:
void removeAttribute(String name)
- 参数:
- name,名称
- 方法:
代码
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; @WebServlet("/s1") public class ServletS1 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取Session对象 HttpSession session = request.getSession(); // 添加session值 session.setAttribute("userName","张三"); // 读取session String value = (String)session.getAttribute("userName"); System.out.println(value); // 张三 // 移除session session.removeAttribute("userName"); // 重写读取 String value2 = (String)session.getAttribute("userName"); System.out.println(value2); // null } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
2.3-Session原理
Session的实现依赖于Cookie。
2.4-Session何时销毁
- 服务器关闭
- session对象调用
invalidate()
- session默认失效时间 30分钟
可以通过配置文件如:web.xml配置session的默认失效如下
<session-config> <session-timeout>30</session-timeout> </session-config>
当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
默认情况不是。
如果需要相同,则可以创建Cookie,键为JSESSIONID,设置最大存活时间,让cookie持久化保存。如下:
Cookie c = new Cookie("JSESSIONID",session.getId()); c.setMaxAge(60*60); response.addCookie(c);
客户端不关闭,服务器关闭后,两次获取的session是同一个吗?
不是同一个,但是要确保数据不丢失。tomcat自动完成以下工作。
- session的钝化:在服务器正常关闭之前,将session对象系列化到硬盘上。
- session的活化:在服务器启动后,将session文件转化为内存中的session对象即可。
2.5-Session的特点
- session用于存储一次会话的多次请求的数据,存在服务器端。
- session可以存储任意类型,任意大小的数据。
2.6-Session和Cookie的区别
- session存储数据在服务器端,Cookie在客户端。
- session没有数据大小限制,Cookie有。
- session数据安全,Cookie相对不安全。
2.7-Session的应用场景
Session用于保存每个用户的专用信息,变量的值保存在服务器端,通过SessionID来区分不同的客户。
- 网上商城中的购物车
- 保存用户登录信息
- 将某些数据放入session中,供同一用户的不同页面使用。
- 防止用户非法登录
2.8-Session的缺点
(1)Session保存的东西越多,就越占用服务器内存,对于用户在线人数较多的网站,服务器的内存压力会比较大;
(2)依赖于cookie(sessionID保存在cookie),如果禁用cookie,则要使用URL重写,不安全;
(3)创建Session变量有很大的随意性,可随时调用,不需要开发者做精确地处理,所以,过度使用session变量将会导致代码不可读而且不好维护。
2.9-案例
需求
登录页面,用户输入用户名和密码以及验证码,验证登录是否成功。
登录失败的提示信息:
- 验证码错误
- 用户名或密码错误
登录成功到首页,显示欢迎XXX
实现步骤
概述
- 准备数据信息(创建数据库、表、设置数据)
- 新建项目,准备导入相关的jar包(Druid相关jar包和JdbcTemplate对象需要的相关jar包)
- 配置文件设置数据库连接信息
数据库准备
CREATE DATABASE db5; USE db5; CREATE TABLE USER( id INT PRIMARY KEY AUTO_INCREMENT, userName VARCHAR(32) NOT NULL, pwd VARCHAR(32) NOT NULL ); INSERT INTO USER VALUES(NULL,zhagnsan,123456),(NULL,lisi,abcdef);
User实体类
package cn.myweb.domain; /** * 用户实体类 * @author Bruce */ public class User { private int id; private String userName; private String pwd; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; }
druid.propertites配置文件
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/db5 username=root password=root initialSize=5 maxActive=10 maxWait=3000
DruidUtils数据库连接池操作类
package cn.myweb.utils; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class DruidUtils { private static DataSource ds; private static InputStream is; static { Properties pro = new Properties(); try { // 读取配置文件 is = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties"); pro.load(is); // 创建数据库连接池对象 ds = DruidDataSourceFactory.createDataSource(pro); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); }finally { if(is!=null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } // 获取Connection对象 public static Connection getConnection() throws SQLException { return ds.getConnection(); } // 释放资源 public static void close(Statement sta, Connection con, ResultSet rs) { if(sta!=null){ try { sta.close(); } catch (SQLException e) { e.printStackTrace(); } } if(con!=null){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } if(rs!=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } public static void close(Statement sta, Connection con){ close(sta,con,null); } // 获取连接池对象 public static DataSource getDataSource(){ return ds; } }
UserDao操作数据库用户表的类
package cn.myweb.Dao; import cn.myweb.domain.User; import cn.myweb.utils.DruidUtils; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; /** * 数据库User表相关操作 */ public class UserDao { // 获取JdbcTemplate对象 JdbcTemplate jt = new JdbcTemplate(DruidUtils.getDataSource()); public User checkUser(User loginUser){ User user = null; String sql = "select * from user where userName=? and pwd=?"; try { user = jt.queryForObject(sql,new BeanPropertyRowMapper<User>(User.class),loginUser.getUserName(),loginUser.getPwd()); }catch (Exception e){ e.printStackTrace(); return null; } return user; } }
生成验证码的ServletCheckCode类
package cn.myweb.myServlet; 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("/ServletCheckCode") public class ServletCheckCode extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int width = 100; int height = 50; // 1.创建一个图片对象 BufferedImage bImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR); // 2.美化图片 // 2.1 填充颜色 // 获取画笔 Graphics graphics = bImg.getGraphics(); // 设置颜色 graphics.setColor(Color.darkGray); // 填充颜色 graphics.fillRect(0, 0, width, height); // 2.2 绘制边框 graphics.setColor(Color.pink); graphics.drawRect(0, 0, width - 1, height - 1); // 2.3 绘制文字 Random random = new Random(); String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"; StringBuilder sb = new StringBuilder(); for (int i = 1; i <= 4; i++) { char charStr = str.charAt(random.nextInt(str.length())); sb.append(charStr); graphics.drawString(charStr + "", width / 5 * i, height / 2 + 6); } String code = sb.toString(); // 将code存入session; HttpSession session = request.getSession(); session.setAttribute("code",code); // 2.4 绘制干扰线 for (int i = 0; i < 10; i++) { float[] fs = {}; graphics.setColor(Color.cyan); int x1 = random.nextInt(width); int x2 = random.nextInt(width); int y1 = random.nextInt(height); int y2 = random.nextInt(height); graphics.drawLine(x1, y1, x2, y2); } // 3.将图片输出到页面 ImageIO.write(bImg, "jpg", response.getOutputStream()); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
ServletLogin登录处理类
package cn.myweb.myServlet; import cn.myweb.Dao.UserDao; import cn.myweb.domain.User; import org.apache.commons.beanutils.BeanUtils; 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.lang.reflect.InvocationTargetException; import java.util.Map; @WebServlet("/ServletLogin") public class ServletLogin extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置request编码格式 request.setCharacterEncoding("utf-8"); // 读取参数 Map<String, String[]> parameterMap = request.getParameterMap(); // 创建User对象 User loginUser = new User(); // 根据参数为User对象赋值 try { BeanUtils.populate(loginUser,parameterMap); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } // 获取用户传入的验证码 String CCode = request.getParameter("code"); // 获取服务器本地验证码 String SCode = (String)request.getSession().getAttribute("code"); // 删除服务器验证码,保证每次提交验证码都是新的 request.getSession().removeAttribute("code"); // 检测验证码是否正确 if(SCode!=null&&SCode.equalsIgnoreCase(CCode)){ // 检测用户名和密码是否正确 User user = new UserDao().checkUser(loginUser); if(user==null){ request.setAttribute("login_error","用户名或密码错误!"); request.getRequestDispatcher("/login.jsp").forward(request,response); }else { request.getSession().setAttribute("user",user); response.sendRedirect("/myweb01/index.jsp"); } }else { // 转发到登录页,告诉用户验证码不正确! request.setAttribute("code_error","验证码输入不正确!"); request.getRequestDispatcher("/login.jsp").forward(request,response); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
登录页面login.jsp
<%-- Created by IntelliJ IDEA. User: Bruce Date: 2019/12/26 Time: 19:00 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Login</title> <style> table{ border-collapse: collapse; width: 585px; margin: 0 auto; } td { padding:20px; } input { height: 50px; } input[type=text],input[type=password]{ width:300px; } table tr:last-child { text-align: center; } input[type=submit]{ width: 100px; } div { color: red; text-align: center; } </style> </head> <body> <form method="post" action="/myweb01/ServletLogin"> <table> <tr> <td>用户名:</td> <td colspan="2"><input type="text" name="userName" placeholder="请输入用户名"></td> </tr> <tr> <td>密码:</td> <td colspan="2"><input type="password" name="pwd" placeholder="请输入密码"></td> </tr> <tr> <td>验证码:</td> <td><input type="text" name="code" placeholder="请输入验证码"></td> <td><img src="/myweb01/ServletCheckCode" alt="验证码" title="看不清?点击换一张"></td> </tr> <tr> <td colspan="3"><input type="submit" value="登录"></td> </tr> </table> </form> <script> var imgNode = document.querySelector("img"); imgNode.onclick = function(){ this.src = "/myweb01/ServletCheckCode?time=" + new Date().getTime(); }; </script> <div><%= request.getAttribute("code_error")==null?"":request.getAttribute("code_error")%></div> <div><%= request.getAttribute("login_error")==null?"":request.getAttribute("login_error")%></div> </body> </html>
首页页面index.jsp
<%@ page import="cn.myweb.domain.User" %><%-- Created by IntelliJ IDEA. User: Bruce Date: 2019/12/26 Time: 18:49 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>首页</title> <style> span { color: green; } </style> </head> <body> <h1>首页</h1> <% User user = (User) session.getAttribute("user"); %> <% if (user != null) {%> <h2>欢迎来<span>【<%=user.getUserName()%>】</span>到MyWeb</h2> <%}%> </body> </html>
来源:https://www.cnblogs.com/lpl666/p/12105274.html