文章目录
一、会话的概念
- 在计算机术语中,会话是指
- 一个终端用户与交互系统进行通讯的过程
- 比如从输入账户密码进入操作系统
- 到退出操作系统就是一个会话过程
- 会话较多用于网络上
- TCP的三次握手就创建了一个会话
- TCP关闭连接就是关闭会话。
二、保存会话数据的两种技术
1、Cookie
- Cookie是客户端技术
- 程序把每个用户的数据
- 以cookie的形式存在用户各自的浏览器
- 当用户使用浏览器再去访问服务器中的web资源时
- 就会带着各自的数据去
- web资源处理的就是用户各自的数据了。
2、Session
- Session是服务器端技术
- 使用这个技术,服务器在运行时
- 可以为每一个用户的浏览器
- 创建一个其独享的session对象
- 由于session为用户浏览器独享
- 所以用户在访问服务器的web资源时
- 可以把各自的数据放在各自的session中
- 当用户再去访问服务器中的其它web资源时
- 其它web资源再从用户各自的session中
- 取出数据为用户服务。
3、Session和Cookie的主要区别
- cookie数据存放在客户的浏览器上,session数据放在服务器上。
- cookie不是很安全,可以进行cookie欺骗,考虑到安全应当使用session。
- session会在一定时间内保存在服务器上。当访问增多,会比较占用服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。
- session和cookie可以结合使用,将登陆信息等重要信息存放为session,其他信息如果需要保留,可以放在cookie中。
三、Cookie
public class CookieDemo extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置服务器端以UTF-8编码进行输出
response.setCharacterEncoding("UTF-8");
//设置浏览器以UTF-8编码进行接收
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
//获取浏览器访问访问服务器时传递过来的cookie数组
Cookie[] cookies = request.getCookies();
//如果用户是第一次访问,那么得到的cookies将是null
if (cookies!=null) {
out.write("您上次访问的时间是:");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
if (cookie.getName().equals("lastAccessTime")) {
Long lastAccessTime =Long.parseLong(cookie.getValue());
Date date = new Date(lastAccessTime);
out.write(date.toLocaleString());
}
}
}else {
out.write("这是您第一次访问本站!");
}
//用户访问过之后重新设置用户的访问时间,存储到cookie中,然后发送到客户端浏览器
Cookie cookie = new Cookie("lastAccessTime", System.currentTimeMillis()+"");
//cookie中存储中文,须使用URLEncoder类里面的encode(String s, String enc)方法进行中文转码
//Cookie cookie = new Cookie("userName", URLEncoder.encode("中国", "UTF-8"));
//获取cookie中的中文数据时,再使用URLDecoder类里面的decode(String s, String enc)进行解码
//URLDecoder.decode(cookies[i].getValue(), "UTF-8")
//设置Cookie的有效期为1天
cookie.setMaxAge(24*60*60);
//将cookie对象添加到response对象中,这样服务器在输出response对象中的内容时就会把cookie也输出到客户端浏览器
response.addCookie(cookie);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- 如果创建了一个cookie,并将他发送到浏览器
- 默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中)
- 用户退出浏览器之后即被删除。
- 若希望浏览器将该cookie存储在磁盘上
- 则需要使用maxAge,并给出一个以秒为单位的时间。
- 将最大时效设为0则是命令浏览器删除该cookie。
四、Session
- 在WEB开发中
- 服务器可以为每个用户浏览器
- 创建一个会话对象(session对象)
- 在需要保存用户数据时
- 服务器程序可以把用户数据
- 写到用户浏览器独占的session中
- 当用户使用浏览器访问其它程序时
- 其它程序可以从用户的session中
- 取出该用户的数据,为用户服务。
1、session实现原理
- 服务器创建session出来后,会将session的id以cookie的形式回写给客户机
- 只要客户机的浏览器不关,再去访问服务器时,都会带着session的id去
- 服务器发现客户机浏览器带session的id过来了,就会使用内存中与之对应的session为之服务。
2、session对象的创建、销毁
- 在程序中第一次调用request.getSession()方法时
- 就会创建一个新的Session
- 可以用isNew()方法来判断Session是不是新创建的
//使用request对象的getSession()获取session,如果session不存在则创建一个
HttpSession session = request.getSession();
//获取session的Id
String sessionId = session.getId();
//判断session是不是新创建的
if (session.isNew()) {
response.getWriter().print("session创建成功,session的id是:"+sessionId);
}else {
response.getWriter().print("服务器已经存在session,session的id是:"+sessionId);
}
- session对象默认30分钟没有使用
- 则服务器会自动销毁session
- 在web.xml文件中可以手工配置session的失效时间
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name></display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 设置Session的有效时间:以分钟为单位-->
<session-config>
<session-timeout>20</session-timeout>
</session-config>
</web-app>
- 当需要在程序中手动设置Session失效时
- 可以手工调用session.invalidate方法摧毁session。
3、使用Session解决表单重复提交
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title>Form表单</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/Controller/FormServlet" method="post">
用户名:<input type="text" name="username">
<input type="submit" value="提交" id="submit">
</form>
</body>
</html>
public class FormServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//客户端是以UTF-8编码传输数据到服务器端的
request.setCharacterEncoding("UTF-8");
String userName = request.getParameter("username");
try {
//让当前的线程睡眠3秒钟,模拟网络延迟而导致表单重复提交的现象
Thread.sleep(3*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("向数据库中插入数据:"+userName);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
①在网络延迟的情况下让用户有时间点击多次submit按钮导致表单重复提交,可以采用JavaScript来防止表单重复提交
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title>Form表单</title>
<script type="text/javascript">
//表单是否已经提交标识,默认为false
var isCommitted = false;
function doSubmit1(){
if(isCommitted==false){
//提交表单后,将表单是否已经提交标识设置为true
isCommitted = true;
//返回true让表单正常提交
return true;
}else{
//返回false那么表单将不提交
return false;
}
}
function dosubmit2(){
//获取表单提交按钮
var btnSubmit = document.getElementById("submit");
//将表单提交按钮设置为不可用,这样就可以避免用户再次点击提交按钮
btnSubmit.disabled= "disabled";
//返回true让表单可以正常提交
return true;
}
</script>
</head>
<body>
<form action="${pageContext.request.contextPath}/controller/FormServlet" onsubmit="return doSubmit1()" method="post">
用户名:<input type="text" name="username">
<input type="submit" value="提交" id="submit">
</form>
</body>
</html>
②表单提交后用户点击【刷新】按钮导致表单重复提交
③用户提交表单后,点击浏览器的【后退】按钮回退到表单页面后进行再次提交
对于②和③的解决方法:
1.生成Token的工具类TokenProcessor
public class TokenProcessor {
/*
*单例设计模式(保证类的对象在内存中只有一个)
*1、把类的构造函数私有
*2、自己创建一个类的对象
*3、对外提供一个公共的方法,返回类的对象
*/
private TokenProccessor(){}
private static final TokenProccessor instance = new TokenProccessor();
/**
* 返回类的对象
* @return
*/
public static TokenProccessor getInstance(){
return instance;
}
/**
* 生成Token
* @return
*/
public String makeToken(){
String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + "";
//数据指纹 128位长 16个字节 md5
try {
MessageDigest md = MessageDigest.getInstance("md5");
byte md5[] = md.digest(token.getBytes());
//base64编码--任意二进制编码明文字符 adfsdfsdfsf
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(md5);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}
2.创建TokenServlet,用于生成Token(令牌)和跳转到form.jsp页面
public class TokenServlet extends HttpServlet {
private static final long serialVersionUID = -884689940866074733L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//创建令牌
String token = TokenProcessor.getInstance().makeToken();
//在服务器使用session保存token(令牌)
request.getSession().setAttribute("token", token);
//跳转到form.jsp页面
request.getRequestDispatcher("/form.jsp").forward(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
3.在form.jsp中使用隐藏域来存储Token(令牌)
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>form表单</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/controller/FormServlet" method="post">
<%--使用隐藏域存储生成的token,也可value="<%=session.getAttribute("token") %>--%>
<input type="hidden" name="token" value="${token}"/>
用户名:<input type="text" name="username">
<input type="submit" value="提交">
</form>
</body>
</html>
4.FormServlet处理表单提交
public class FormServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//判断用户是否是重复提交
boolean b = isRepeatSubmit(request);
if(b==true){
System.out.println("请不要重复提交");
return;
}
//移除session中的token
request.getSession().removeAttribute("token");
System.out.println("处理请求");
}
private boolean isRepeatSubmit(HttpServletRequest request) {
String client_token = request.getParameter("token");
//1、如果用户提交的表单数据中没有token,则用户是重复提交了表单
if(client_token==null){
return true;
}
//取出存储在Session中的token
String server_token = (String) request.getSession().getAttribute("token");
//2、如果当前用户的Session中不存在Token(令牌),则用户是重复提交了表单
if(server_token==null){
return true;
}
//3、存储在Session中的Token(令牌)与表单提交的Token(令牌)不同,则用户是重复提交了表单
if(!client_token.equals(server_token)){
return true;
}
return false;
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}