会话技术
- 会话: 一次会话中包含多次请求和响应
- 功能
- 方式
- Cookie
- 基础使用步骤
- 修改servlet模板代码,方便编程
- 代码演示
- Cookie实现原理
- 因为cookie是基于响应头set_cookie实现的,因此我们可以直接通过设置响应头的方式,完成cookie的发送
- 一旦发送给浏览器某个cookie对象后,那么在一次会话间,每次访问这个网站时,请求头中都会带上这个cookie对象
- Cookie细节
- 一次发送多个Cookie
- Cookie在浏览器中保存的时间
- cookie默认在会话期间有效,只要浏览器不关闭,cookie就存在,即cookie存在于浏览器的进程之中
- cookie的修改和删除都是同名cookie覆盖
- Cookie的持久化存储
- Cookie能不能存储中文数据
- Cookie共享问题
- Cookie的特点和作用
- 案例: 记住上一次访问时间
- 注意: cookie不支持特殊字符,因此需要采用URL编码存储,用URL解码来解析
- Session
- 概念
- 一次会话的概念
- HttpSession对象---共享数据
- Session原理
- 如果第一次获取session,此时没有cookie,会创建一个session,然后将该session的id响应给浏览器,浏览器创建一个cookie来保存这个id
- 如果第二次获取session,此时有cookie,便会去找cookie要来id,这样就可以在服务器端找到对应的session对象
- 如果第二次获取session时,没了cookie,便会执行第一次获取session的步骤
- 如果第一次访问index页面,因为session时内置对象,因此会创建一个session对象
- 当客户端关闭后,服务器不关闭,两次获取session是否为同一个
- 如果没有设置cookie的持久化保存,那么浏览器关闭后,cookie销毁,那么cookie里面记录的JSESSIONID也随之销毁,下次打开浏览器发现cookie没了,也就无法得到对应的cookie里面记录的JSESSIONID,那么就会新创建一个cookie,保存新创建的session的id
- 客户端不关闭后,服务器关闭,两次获取session是否为同一个
- Session被销毁时机
- Session有默认的失效时间,服务器关闭session不会被销毁,并且可以有多个session同时存在的请求,因为浏览器每次关闭,再打开时,如果需要session对象,发现id没了,会新创建一个cookie保存新sesssion的id。那么之前cookie对象里面保存的id消失了,也就找不回原来的session,但是他还活着
- session特点
- session与cookie的区别
- 验证码案例
- 1.案例需求
- 2.案例分析
- 完整代码
- 总结
- 令牌机制
会话: 一次会话中包含多次请求和响应
一次会话: 浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止
功能
在一次会话的范围内的多次请求间共享数据
方式
- 客户端会话技术: cookie 2.服务器端会话技术; session
Cookie
概念: 客户端会话技术,将数据保存到客户端
基础使用步骤
修改servlet模板代码,方便编程
第一步:
第二步:
第三步:
第四步:
代码演示
CookieServlet1: ---->发送Cookie方
代码语言:javascript复制@WebServlet("/CookieServlet1")
public class CookieServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.创建Cookie对象
Cookie c=new Cookie("msg","hello");
//2.发送Cookie
response.addCookie(c);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
CookieServlet1:接收Cookie方
代码语言:javascript复制@WebServlet("/CookieServlet")
public class CookieServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取Cookie
Cookie[] cs = request.getCookies();
if(cs!=null)
{
for(Cookie c:cs)
{
String name = c.getName();
String value = c.getValue();
System.out.println(name " " value);
}
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
Cookie实现原理
基于响应头set_cookie和请求头cookie实现
因为cookie是基于响应头set_cookie实现的,因此我们可以直接通过设置响应头的方式,完成cookie的发送
一旦发送给浏览器某个cookie对象后,那么在一次会话间,每次访问这个网站时,请求头中都会带上这个cookie对象
第一次访问服务器,响应头会返回 set-Cookie:
第二次访问,会带着上次请求返回的 Cookie 访问:
也需要注意,第一次 访问 Cookie1,接收到返回的 set-Cookie,第二次不是只有访问 cookie2 ,才能在响应头里看到Cookie,第二次访问哪个都会被携带,这里只是,我们在 Cookie2 这个页面设置了接收 Cookie 的数组,会被打印出来而已。
Cookie细节
一次发送多个Cookie
代码语言:javascript复制创建多个Cookie对象,使用response调用多次addcookie方法发送cookie即可
//1.创建Cookie对象
Cookie c=new Cookie("msg","hello");
Cookie c1=new Cookie("msg","hello");
//2.发送Cookie
response.addCookie(c);
response.addCookie(c1);
Cookie在浏览器中保存的时间
默认情况下,浏览器被关闭后,cookie数据被销毁
cookie默认在会话期间有效,只要浏览器不关闭,cookie就存在,即cookie存在于浏览器的进程之中
cookie的修改和删除都是同名cookie覆盖
Cookie的持久化存储
代码语言:javascript复制setMaxAge(int seconds)--->秒
- 正数: 将Cookie数据写到硬盘文件中,持久化存储. Cookie存活时间
- 负数:默认值,会话cookie,跟随浏览器的进程
- 0:删除Cookie信息
Cookie能不能存储中文数据
- tomcat 8版本之前,Cookie中不能直接存储中文数据 需要将中文数据转码,一般采用URL编码(�)
- tomcat 8版本之后,Cookie中支持中文数据 特殊字符还是不支持,建议使用URL编码存储,URL编码解析,例如空格
Cookie共享问题
1 假设在一个tomcat服务器中,部署了多个web项目,那么这些web项目中cookien能不能共享?
- 默认情况下cookie不能共享
setPath(String path): 设置Cookie的共享范围. 默认情况下,设置为当前的虚拟目录
如果要共享,可以将Cookie设置为"/"
/----->当前项目的根路径—>localhost/
注意: 以上方法是用来解决同一个服务器下,多个web项目的Cookie共享问题
2 不同tomcat服务器间Cookie共享问题 ?
代码语言:javascript复制setDomain(String path):如果设置一级域名相同,那么多个服务器之间Cookie可以共享
例如:
代码语言:javascript复制setDomain(".baidu.com"),那么tieba.baidu.com和news.baidu.com中的Cookie可以共享
Cookie的特点和作用
特点:
- Cookie存储数据在客户端浏览器
- 浏览器对于单个Cookie的大小由限制(4kb),以及对同一域名下的总Cookie数量也有限制(20)
作用:
- Cookie一般用于存储少量的不太敏感的数据
- 在不登录的情况下,完成服务器对客户端的身份识别
案例: 记住上一次访问时间
注意: cookie不支持特殊字符,因此需要采用URL编码存储,用URL解码来解析
代码加注释:
代码语言:javascript复制package com.example.Cookie1;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
@WebServlet("/CookieServlet")
public class CookieServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置响应消息体的数据格式以及编码
response.setContentType("text/html;charset=utf-8");
//判断是否有cookie的值为lastTime
boolean flag=false;
//获取所有Cookie
Cookie[] cookies = request.getCookies();
//遍历Cookie数组
if(cookies!=null&&cookies.length>0)
{
for(Cookie c:cookies)
{
//获取cookie的名称
String name = c.getName();
//判断名称是否是lastTime
if("lastTime".equals(name))
{
//有该cookie,说明不是第一次访问
flag=true;//有lastTime的cookie
//响应数据
//获取cookie的value---->时间
String value = c.getValue();
System.out.println("解码前:" value);
//URL: 解码
value= URLDecoder.decode(value, "utf-8");
System.out.println("解码后:" value);
response.getWriter().write("<h1>欢迎回来,您上一次访问的时间为:" value "</h1>");
//设置Cookie的value
//获取当前时间的字符串,重新设置cookie的值,重新发送cookie
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年mm月dd日 HH:mm:ss");
String format = simpleDateFormat.format(date);
//URL编码
System.out.println("编码前: " format);
format=URLEncoder.encode(format,"utf-8");//
System.out.println("编码后:" format);
//设置cookie的值
c.setValue(format);
//设置cookie的存活时间---一个月
c.setMaxAge(60*60*24*30);
//发送cookie
response.addCookie(c);
break;
}
}
}
if(cookies==null||cookies.length==0||flag==false)
{
//没有,说明是第一次访问
//设置Cookie的value
//获取当前时间的字符串,重新设置cookie的值,重新发送cookie
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年mm月dd日 HH:mm:ss");
String format = simpleDateFormat.format(date);
Cookie c=new Cookie("lastTime",format);
//URL编码
System.out.println("编码前: " format);
format=URLEncoder.encode(format,"utf-8");//
System.out.println("编码后:" format);
//设置cookie的值
c.setValue(format);
//设置cookie的存活时间---一个月
c.setMaxAge(60*60*24*30);
//发送cookie
response.addCookie(c);
response.getWriter().write("<h1>欢迎首次访问</h1>");
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
Session
概念
服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。 HttpSession
一次会话的概念
一次会话,什么是一次会话呢?我举个例子:就我们现在上网,打开浏览器浏览网站当时开始到你关闭浏览器,就称之为一次会话;
HttpSession对象—共享数据
1 获取HttpSession对象
代码语言:javascript复制 request.getsession()
2 使用HttpSession对象
代码语言:javascript复制Object getAttribute(String name)
void setAttribute(String name,Object value)
void removeAttribute(String name)
代码演示:
Demo1Servlet :
代码语言:javascript复制@WebServlet("/SessionDemo1")
public class SessionDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用session共享数据
//1.获取session数据
HttpSession session = request.getSession();
//2.存储数据
session.setAttribute("msg","大忽悠");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
Demo2Servlet:
代码语言:javascript复制@WebServlet("/SessionDemo2")
public class SessionDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取session
HttpSession session = request.getSession();
//2.获取数据
Object msg = session.getAttribute("msg");
System.out.println(msg);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
Session原理
如果第一次获取session,此时没有cookie,会创建一个session,然后将该session的id响应给浏览器,浏览器创建一个cookie来保存这个id
如果第二次获取session,此时有cookie,便会去找cookie要来id,这样就可以在服务器端找到对应的session对象
如果第二次获取session时,没了cookie,便会执行第一次获取session的步骤
如果第一次访问index页面,因为session时内置对象,因此会创建一个session对象
Session的实现是依赖与Cookie的
当客户端关闭后,服务器不关闭,两次获取session是否为同一个
如果没有设置cookie的持久化保存,那么浏览器关闭后,cookie销毁,那么cookie里面记录的JSESSIONID也随之销毁,下次打开浏览器发现cookie没了,也就无法得到对应的cookie里面记录的JSESSIONID,那么就会新创建一个cookie,保存新创建的session的id
默认情况下不是,因为客户端关闭后,一次会话就结束了.
如果需要相同,则可以创建Cookie,键为JSESSIONID,让Cookie持久化保存
代码演示:
代码语言:javascript复制 //1.获取session
HttpSession session = request.getSession();
Cookie c=new Cookie("JSESSIONID",session.getId());
c.setMaxAge(60*60);
response.addCookie(c);
//2.获取数据
Object msg = session.getAttribute("msg");
System.out.println(msg);
客户端不关闭后,服务器关闭,两次获取session是否为同一个
不是同一个,但是要确保数据不丢失
1.session钝化: 在服务器正常关闭之前,将session对象系列化到硬盘上
2.session活化: 在服务器启动后,将session文件转化为内存中的session对象即可
tomcat服务器会自动完成钝化和活化过程,但是IDEA不会,因此一般将项目直接放到tmocat的webapps目录下
Session被销毁时机
Session有默认的失效时间,服务器关闭session不会被销毁,并且可以有多个session同时存在的请求,因为浏览器每次关闭,再打开时,如果需要session对象,发现id没了,会新创建一个cookie保存新sesssion的id。那么之前cookie对象里面保存的id消失了,也就找不回原来的session,但是他还活着
session对象调用invalidate()来销毁自己
session默认失效时间为30分钟
我们可以对默认失效时间进行修改:
web.xml配置文件目录,大概500-600多行:
session特点
- session用于存储一次会话的多次请求的数据,存储在服务器端
- session可以存储任意类型,任意大小的数据
session与cookie的区别
- session存储数据在服务器端,cookie在客户端
- session没有数据大小限制,而cookie有
- session数据安全,cookie相对于不安全
验证码案例
1.案例需求
2.案例分析
在访问浏览器时,会有两个请求,因为验证码图片试一次单独的请求
完整代码
需要导入的jar包:
配置文件:
代码语言:javascript复制driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///test1
username=root
password=xxxx
#初始化连接数量
initialSize=5
#最大连接数
maxActive=10
#最大等待时间
maxWait=3000
login.jsp
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>战地六游戏官网</title>
<script>
window.onload=function(){
document.getElementById("img").onclick=function (){
this.src="/ZhanDi/CheckCodeServlet?time=" new Date().getTime();
}
}
</script>
<style>
div{
color: red;
}
</style>
</head>
<body>
<form action="/ZhanDi/LoginServlet" method="get">
<table>
<tr>
<th>用户名:</th>
<td><input type="text" placeholder="请输入用户名" name="username"></td>
</tr>
<tr>
<th>密码:</th>
<td><input type="password" placeholder="请输入密码" name="password"></td>
</tr>
<tr>
<th>验证码:</th>
<td><input type="text" placeholder="请输入验证码" name="checkcode"></td>
</tr>
<tr>
<td colspan="2"> <img id="img" src="/ZhanDi/CheckCodeServlet"></td>
</tr>
<tr>
<td colspan="2"> <input type="submit" value="登录"></td>
</tr>
</table>
</form>
<div><%=request.getAttribute("yzm_error")==null?"":request.getAttribute("yzm_error")%></div>
<div><%=request.getAttribute("dl_error")==null?"":request.getAttribute("dl_error")%></div>
</body>
</html>
success.jsp
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>战地1</title>
</head>
<body>
<h1>欢迎回来,<%=request.getSession().getAttribute("user")%></h1><br>
<img src="/zd.png"/>
</body>
</html>
CheckCode目录下:
1.CheckCodeServlet
代码语言:javascript复制package Checkcode;
import javax.imageio.ImageIO;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
@WebServlet("/CheckCodeServlet")
public class CheckCodeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int width=150;
int height=50;
//1.在内存中创建一个图片----创建一个不带透明色的对象
BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//2.填充背景色
Graphics g=image.getGraphics();//画笔对象
g.setColor(Color.PINK);
g.fillRect(0,0,width,height);
//3.画边框
g.setColor(Color.BLUE);
g.drawRect(0,0,width-1,height-1);
//4.随机生成验证码
String str="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random ran=new Random();
StringBuilder sb=new StringBuilder();
for(int i=1;i<=4;i )
{
//生成随机角标
int index=ran.nextInt(str.length());
//获取字符
char ch=str.charAt(index);
//将每个验证码字符加入sb中
sb.append(ch);
//写验证码
g.drawString(ch " ",width/5*i,height/2);
}
//获取验证码字符串
String checkCode_session= sb.toString();
//将验证码存入session
request.getSession().setAttribute("checkCode_session",checkCode_session);
//7.画干扰线
//随机生成坐标点
for(int i=0;i<4;i )
{
int x1=ran.nextInt(width);
int x2=ran.nextInt(width);
int y1=ran.nextInt(height);
int y2=ran.nextInt(height);
g.setColor(Color.RED);
g.drawLine(x1,y1,x2,y2);
}
//8.将图片输出到页面上----第二个参数是后缀名,第三个参数是一个输出流
ImageIO.write(image,"jpg",response.getOutputStream());
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
2.loginServlet
代码语言:javascript复制package Checkcode;
import mySQL.UserDao;
import org.apache.commons.beanutils.BeanUtils;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置request编码----和页面的编码统一
request.setCharacterEncoding("utf-8");
//获取参数Map
Map<String, String[]> parameterMap = request.getParameterMap();
//创建User对象
User user=new User();
//使用BeanUtils工具类的populate方法将map集合封装成一个user对象
try {
BeanUtils.populate(user,parameterMap);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//判断验证码输入的是否正确
//1.先获取程序生成的验证码
HttpSession session = request.getSession();
String checkCode_session = (String)session.getAttribute("checkCode_session");
//删除session中存储的验证码,确保验证码是一次性的
session.removeAttribute("checkCode_session");
//测试代码
System.out.println("程序生成的验证码:" checkCode_session);
System.out.println("用户输入的验证码:" user.getCheckcode());
System.out.println("用户名:" user.getUsername() " " "密码:" user.getPassword());
//验证码忽略大小写
if(checkCode_session!=null&&checkCode_session.equalsIgnoreCase(user.getCheckcode()))
{
System.out.println("验证码正确");
//验证码正确
//判断用户名和密码是否一致
//通过在数据库中查询来判断
User u=UserDao.check(user);
if(u!=null&&user.getUsername().equals(u.getUsername())&&user.getPassword().equals(u.getPassword()))
{
System.out.println("登录成功");
//登录成功
//存储用户信息---两次请求,要把信息存储到session里面
session.setAttribute("user",user.getUsername());
//重定向到success.jsp---动态获取虚拟目录
response.sendRedirect(request.getContextPath() "/success.jsp");
}
else
{
System.out.println("登录失败");
//登录失败
//存储信息到request
request.setAttribute("dl_error","用户名或密码错误");
//转发到登录页面
request.getRequestDispatcher("/login.jsp").forward(request,response);
}
}
else
{
//验证码不一致
//存储提示信息到request,然后在页面上面显示
request.setAttribute("yzm_error","验证码错误");
//转发到登录页面----不需要写虚拟目录
request.getRequestDispatcher("/login.jsp").forward(request,response);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
3.user
代码语言:javascript复制package Checkcode;
public class User {
private String username;
private String password;
private String checkcode;
public User() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getCheckcode() {
return checkcode;
}
public void setCheckcode(String checkcode) {
this.checkcode = checkcode;
}
@Override
public String toString() {
return "User{"
"username='" username '''
", password='" password '''
", checkcode='" checkcode '''
'}';
}
}
MySQL:
JDBCUtils:
代码语言:javascript复制package mySQL;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JDBCUtils {
//1.定义成员变量
private static DataSource ds;
static {
try {
//1.加载配置文件
Properties pro=new Properties();
pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
//2.获取DataSource
ds= DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//释放资源
public static void close(ResultSet res,Statement stmt, Connection conn)
{
if(res!=null)
{
try {
res.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt!=null)
{
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null)
{
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
public static void close(Statement stmt, Connection conn)
{
close(null,stmt,conn);
}
public static DataSource getDataSource()
{
return ds;
}
}
UserDao
代码语言:javascript复制package mySQL;
import Checkcode.User;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
/*
*在mysql数据库中查询相关的用户
* */
public class UserDao
{
//声明JDBCTemplate对象共用
static private JdbcTemplate template=new JdbcTemplate(JDBCUtils.getDataSource());
//查询用户
static public User check(User loginuser)
{
try
{
//编写查询的sql语句
String sql="select* from loginuser where username= ? and password= ?";
//调用query方法---返回一个查到的user对象---user对象唯一,因此使用queryForObject返回查询到的唯一的user对象
//该方法必须返回一个结果,否则报错
User user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), loginuser.getUsername(),loginuser.getPassword());
System.out.println("数据库查到对应用户");
return user;
}
catch (DataAccessException e)
{
//没有查询到
System.out.println("数据库没有查到对应用户");
return null;
}
}
}
注意:
效果演示:
总结
cookie失效就好像客户(浏览器)把银行卡丢了(cookie失效了),但是银行(服务器端)里面还有对应的账号存在
为了防止弄丢银行卡,因此需要告诫客户好好保存(cookie持久化技术)
session失效就好像银行账号失效了(session失效),虽然客户(浏览器)有卡(cookie没有失效)但是卡已经用不了
令牌机制
java 防止表单的重复提交-令牌机制