Java Web学习总结(6)Cookie/Session
一、会话的概念
会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。
二、会话过程中要解决的一些问题
每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,程序要想办法为每个用户保存这些数据。
三、保存会话数据的两种技术
1、Cookie
Cookie意为"甜饼",是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。
由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。
Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
2、Session
Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务。
四,Cookie类的主要方法
int getMaxAge() |
返回Cookie过期之前的最大时间,以秒计算。 |
void setMaxAge(int expiry) |
以秒计算,设置Cookie过期时间。 |
String getName() |
返回Cookie的名字。名字和值是我们始终关心的两个部分,笔者会在后面详细介绍 getName/setName。 |
void setValue(String newValue) |
Cookie创建后设置一个新的值。 |
String getValue() |
返回Cookie的值。笔者也将在后面详细介绍getValue/setValue。 |
void setDomain(String pattern) |
设置cookie中Cookie适用的域名 |
String getDomain() |
返回Cookie中Cookie适用的域名. 使用getDomain() 方法可以指示浏览器把Cookie返回给同 一域内的其他服务器,而通常Cookie只返回给与发送它的服务器名字完全相同的服务器。注意域名必须以点开始(例如.yesky.com) |
void setPath(String uri) |
指定Cookie适用的路径。 |
String getPath() |
返回Cookie适用的路径。如果不指定路径,Cookie将返回给当前页面所在目录及其子目录下 的所有页面。 |
void setSecure(boolean flag) |
指出浏览器使用的安全协议,例如HTTPS或SSL。 |
boolean getSecure() |
如果浏览器通过安全协议发送cookies将返回true值,如果浏览器使用标准协议则返回false值。 |
void setVersion(int v) |
设置Cookie所遵从的协议版本。 |
int getVersion() |
返回Cookie所遵从的协议版本。 |
void setComment(String purpose) |
设置cookie中注释。 |
String getComment() |
返回Cookie中注释,如果没有注释的话将返回空值。 |
Cookie(String name, String value) |
实例化Cookie对象,传入cooke名称和cookie的值。 |
response接口也中定义了一个addCookie方法,它用于在其响应头中增加一个相应的Set-Cookie头字段。 同样,request接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。
五,Cookie使用
1,使用cookie记录用户上一次访问的时间
public class CookieDemo extends HttpServlet{
private static final long serialVersionUID = 5757885987685925915L; @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置服务器以UTF-8编码输出
resp.setCharacterEncoding("UTF-8");
//设置浏览器以UTF-8编码进行接收
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter(); //获取Cookie数组
Cookie[] cookie = req.getCookies();
if(cookie == null){
out.write("这是你的第一次访问!");
} else {
for (Cookie ck : cookie) {
if(ck.getName().equals("cookieName")){
//获取Cookie里面保存的数据
Long time = Long.parseLong(ck.getValue());
Date date = new Date(time);
out.write("上次访问时间:" + date.toLocaleString());
}
}
} //创建一个cookie,cookie的名字是cookieName
Cookie cookies = new Cookie("cookieName", System.currentTimeMillis()+"");
//设置Cookie的有效期为1天,这样即使关闭了浏览器,下次再访问时,也依然可以通过cookie获取用户上一次访问的时间。
cookies.setMaxAge(24*60*60);
//将cookie对象添加到response对象中
resp.addCookie(cookies);
}
}
第一次访问时,如下所示:
再次访问:
2,删除Cookie
public class CookieDemo extends HttpServlet { @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//创建一个名字为lastAccessTime的cookie
Cookie cookie = new Cookie("lastAccessTime", System.currentTimeMillis()+"");
//将cookie的有效期设置为0,命令浏览器删除该cookie
cookie.setMaxAge(0);
req.addCookie(cookie);
}
}
3,cookie中存/取中文
public class CookieDemo2 extends HttpServlet{
private static final long serialVersionUID = 5757885987685925915L; @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置服务器以UTF-8编码输出
resp.setCharacterEncoding("UTF-8");
//设置浏览器以UTF-8编码进行接收
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter(); //创建一个cookie,cookie的名字是cookieName
//存储中文时,使用URLEncoder类里面的encode(String s, String enc)方法进行中文转码
Cookie cookies = new Cookie("cookieName", URLEncoder.encode("哎哟!不错哟", "UTF-8"));
//将cookie对象添加到response对象中
resp.addCookie(cookies); //获取Cookie数组
Cookie[] cookie = req.getCookies();
if(cookie != null){
for (Cookie ck : cookie) {
if(ck.getName().equals("cookieName")){
//获取Cookie里面保存的数据
String text = ck.getValue();
//使用URLDecoder类里面的decode(String s, String enc)进行解码
out.write("存储的中文数据:" + URLDecoder.decode(text, "UTF-8"));
}
}
}
}
}
结果如下:
Cookie注意细节
1,一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
2,一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。
3,浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。
4,如果创建了一个cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。若希望浏览器将该cookie存储在磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间。将最大时效设为0则是命令浏览器删除该cookie。
六,Session简单介绍
在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。
Session和Cookie的主要区别
1,Cookie是把用户的数据写给用户的浏览器。
2,Session技术把用户的数据写到用户独占的session中。
3,Session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。
七,Session基础知识
Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务。
当用户打开浏览器,访问某个网站操作session时,服务器就会在服务器的内存为该浏览器分配一个session对象,该session对象被这个浏览器独占。
这个session对象也可以看做是一个容器,session默认存在时间为30min,你可以修改。
1,Session可以用来做什么
1,网上商城中的购物车
2,保存登录用户的信息
3,将某些数据放入到Session中,供同一用户的各个页面使用
4,防止用户非法登录到某个页面。
2,Session基本使用
request.getSession() |
返回这个request绑定的session对象,如果没有,则创建一个 |
request.getSession(boolean create) |
返回这个request绑定的session对象,如果没有,则根据create的值决定是否创建一个 |
session.setAttribute(String name,Object val) |
向session中添加属性 |
session.getAttribute(String name) |
从session中得到某个属性 |
session.removeAttribute(String name) |
从session中删除某个属性 |
session.setMaxInactiveInterval() |
设置Session的生命周期(单位秒),Session的生命周期默认是30min |
session.invalidate() |
清除所有session |
session.removeAttribute(String name) |
删除指定名称的session |
Servlet1:
public class Servlet1 extends HttpServlet {
private static final long serialVersionUID = -8236507185410764108L; @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session
HttpSession session = req.getSession();
//将数据存储到session中
session.setAttribute("name", "Zender");
}
}
Servlet2:
public class Servlet2 extends HttpServlet {
private static final long serialVersionUID = -8236507185410764108L; @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session
HttpSession session = req.getSession();
//获取name
String name = (String) session.getAttribute("name");
PrintWriter out = resp.getWriter();
out.print("name:" + name);
}
}
同一浏览器访问Servlet1,再访问Servlet2,结果如下:
不同浏览器访问Servlet1,再访问Servlet2,结果如下:
可以看到这时候name是null,也就是没有从session对象中取出值,因为360浏览器并没有运行Servlet1来创建Session对象,上面的session对象是Chrome浏览器独占的。
3,Session生命周期
Session中的属性的默认生命周期是30min,这个默认时间可以通过修改web.xml文件来修改
1,在Tomcat根目录\conf\web.xml文件中修改
<session-config> <session-timeout>30</session-timeout> </session-config> |
2,如果只需要对某一个web应用设置,则只需要修改对应web应用的web.xml文件。在这个web.xml文件中添加如上的代码:
<session-config> <session-timeout>10</session-timeout> </session-config> |
除了设置默认生命周期之外,最重要的是在程序中设置,调用setMaxInacttiveInterval(int interval),这里的interval是以秒为单位的,而且这个方法设置的是发呆时间,比如你设置的是60秒,那么在这60秒之内如果你没有操作过session,它就会自动删除,如果你操作过,不管是设置属性还是读取属性,它都会从头开始计时。
session.setMaxInactiveInterval(60); |
八,Session实现原理
服务器是如何实现一个session为一个用户浏览器服务的?
1,浏览器A先访问Servlet1,这时候它创建了一个Session,ID号为110,然后Servlet1将这个ID号以Cookie的方式返回给浏览器A。
2,浏览器A继续访问Servlet2,那么这个请求会带上Cookie值: JSESSIONID=110,然后服务器根据浏览器A传递过来的ID号找到内存中的这个Session。
3,浏览器B来访问Servlet1了,它的请求并没有带上 JSESSIONID这个Cookie值,由于它也要使用Session,所以服务器会新创建一个Session,ID号为111。
4,浏览器B继续访问Servlet2,那么这个请求会带上Cookie值: JSESSIONID=111,然后服务器根据浏览器B传递过来的ID号找到内存中的这个Session。
例如:
Servlet1:
public class Servlet1 extends HttpServlet {
private static final long serialVersionUID = -8236507185410764108L; @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session
HttpSession session = req.getSession();
//将数据存储到session中
session.setAttribute("name", "Zender");
PrintWriter out = resp.getWriter();
out.print("create Session OK");
}
}
Servlet2:
public class Servlet2 extends HttpServlet {
private static final long serialVersionUID = -8236507185410764108L; @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session
HttpSession session = req.getSession();
//获取name
String name = (String) session.getAttribute("name");
PrintWriter out = resp.getWriter();
out.println("Session ID:" + session.getId());
out.print("name:" + name);
}
}
第一次访问Servlet1时,服务器会创建一个新的sesion,并且把session的Id以cookie的形式发送给客户端浏览器,如下图所示:
可以看到,Request Headers中并没有Cookie的信息,而Response Headers中有这么一句话:
Set-Cookie: JSESSIONID=05A94199DDC64311563740CC2C78D656; Path=/CookieAndSession/; HttpOnly |
说明这个时候服务器向客户端通过Cookie传递回了 JSESSIONID这个属性。
然后访问Servlet2,如下图所示:
可以看到Response Headers中没有出现Set-Cookie这个头,而Request Headers中带上了Cookie这个头:
Cookie: JSESSIONID=05A94199DDC64311563740CC2C78D656 |
而这个头中正包含 JSESSIONID,并且它的值也就是我们之前Set-Cookie中 JSESSIONID的值。
这就证明了我们之前图解的Session的原理,也就是服务器能够为不同的浏览器区分不同的Session的机制。
九,Session的简单应用
1,用户登录时候验证验证码
Index.jsp:
<head>
<base href="<%=basePath%>" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>表单提交</title>
<link href="css/bootstrap.css" rel="stylesheet">
<script src="js/jquery-3.2.1.js"></script>
<script src="js/bootstrap.js"></script>
</head>
<body>
<form class="form-horizontal" action="<%=request.getContextPath()%>/CodeServlet.html" role="form" method="post">
<div class="form-group">
<label for="firstname" class="col-sm-1 control-label">验证码</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="idCodeNum"
placeholder="请输入验证码">
</div>
<img src="idCode" onclick="this.src+=''" style="cursor:pointer;" width="115" height="30" title="看不清?换一个">
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-3">
<button type="submit" class="btn btn-default">提交</button>
<button type="reset" class="btn btn-default">重置</button>
</div>
</div>
</form>
</body>
</html>
CodeServlet:
public class CodeServlet extends HttpServlet {
private static final long serialVersionUID = -8236507185410764108L; @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session
HttpSession session = req.getSession();
//获取存储的验证码
String idCodeText = (String) session.getAttribute("idCode");
System.out.println("服务端验证码:" + idCodeText); String idCodeNum = req.getParameter("idCodeNum");
System.out.println("输入的验证码:" + idCodeNum);
//设置浏览器以UTF-8编码进行接收
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
if(idCodeText.equalsIgnoreCase(idCodeNum)){
out.println("验证成功!");
} else {
out.println("验证码错误!");
}
}
}
Web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>CookieAndSession</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>CodeServlet</servlet-name>
<servlet-class>com.zender.session.CodeServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CodeServlet</servlet-name>
<url-pattern>/CodeServlet.html</url-pattern>
</servlet-mapping>
<!-- 生成验证码的Servlet -->
<servlet>
<servlet-name>ValidateCode</servlet-name>
<servlet-class>org.jelly.image.ValidateCode</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ValidateCode</servlet-name>
<url-pattern>/idCode</url-pattern>
</servlet-mapping>
</web-app>
这里使用了jelly-core-1.7.0.GA.jar来生成了验证码,具体使用方式请点击:jelly-core-1.7.0.GA.jar
访问http://localhost:8081/CookieAndSession/index.jsp输入验证码,点击提交:
输入正确验证码,页面响应结果:
输入错误验证码,页面响应结果:
输入正确验证码,后台结果:
输入错误验证码,后台结果:
2,实现简易购物车
模拟一个数据库:
public class DB {
private static HashMap<String, Book> hm = null;
private DB(){
}
static{
hm = new LinkedHashMap<String, Book>();
Book book1 = new Book(1, "Java基础");
Book book2 = new Book(2, "Oracle数据库");
Book book3 = new Book(3, "C语言");
Book book4 = new Book(4, "Python核心教程");
Book book5 = new Book(5, "Web技术");
hm.put(book1.getId() + "" , book1);
hm.put(book2.getId() + "" , book2);
hm.put(book3.getId() + "" , book3);
hm.put(book4.getId() + "" , book4);
hm.put(book5.getId() + "" , book5);
}
/**
* 得到数据库中所有的书
* @return
*/
public static HashMap<String, Book> getBooks(){
return hm;
}
/**
* 根据ID得到书
* @param id
* @return
*/
public static Book getBookById(String id){
if(hm.containsKey(id)){
return hm.get(id);
}
return null;
}
}
ShowBookServlet这个Servlet中读取数据库中所有的书的信息,显示在页面上:
public class ShowBookServlet extends HttpServlet {
private static final long serialVersionUID = -8236507185410764108L; @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
out.println("<h2>Zender书店</h2>");
out.println("<table>");
HashMap<String, Book> books = DB.getBooks();
Iterator it = books.keySet().iterator();
while(it.hasNext()){
Book book = books.get(it.next());
out.println("<tr><td>"+book.getName()+"</td><td><a href='"+ req.getContextPath() +"/BuyBookServlet.html?id="+book.getId()+"'>点击购买</a></td></tr>");
}
out.println("</table>");
}
}
BuyBookServlet这个Servlet用于购买图书:
public class BuyBookServlet extends HttpServlet {
private static final long serialVersionUID = -8236507185410764108L; @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
String id = req.getParameter("id");
//得到用户想买的书
Book book = DB.getBookById(id);
HttpSession session = req.getSession();
//得到用户用于保存所有书的容器
List<Book> list = (ArrayList<Book>) session.getAttribute("list");
if(list == null){
list = new ArrayList<Book>();
session.setAttribute("list", list);
}
list.add(book);
session.setAttribute("books", list);
// 转发到ShowMyCartServlet查看购物车
req.getRequestDispatcher("/ShowMyCartServlet.html").forward(req, resp);
}
}
ShowMyCartServlet这个Servlet用于查看购物车:
//显示购买的图书
public class ShowMyCartServlet extends HttpServlet {
private static final long serialVersionUID = -8236507185410764108L; @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
HttpSession session = req.getSession();
List<Book> list = (ArrayList<Book>) session.getAttribute("books");
if(list==null || list.size()==0){
out.write("对不起,您还没有购买任何商品!!");
return;
} //显示用户买过的商品
out.write("您买过如下商品:<br/>");
for(Book book : list){
out.write(book.getName() + "<br/>");
}
}
}
运行结果:
3,防止用户非法登录到某个页面
比如我们的用户管理系统,必须要登录成功后才能跳转到主页面,而不能直接绕过登录页面直接到主页面,这个应用是一个非常常见的应用。
当在验证用户的控制器LoginClServlet.java验证用户成功后,将当前的用户信息保存在Session对象中:
// 把user对象保存在session HttpSession session = req.getSession(); session.setAttribute("loginUser", user);
然后在主页面Main.java最开始的地方,取出Session中的登录用户信息,如果信息为空,则为非法访问,直接跳转到登录页面,并提示相关信息:
// 取出login-user这个session
User loginUser = (User)req.getSession().getAttribute("loginUser");
if(loginUser == null){
// 说明用户没有登录,让他跳转到登录页面
req.setAttribute("error", "请登录!");
req.getRequestDispatcher("/LoginServlet").forward(req,res);
return;
}
那么这里就存在一个问题,一个网站会有很多个需要防止非法访问的页面,如果都是用这种方法岂不是很麻烦?
两种解决办法:
第一种:将这段验证用户的代码封装成函数,每次调用
第二种:使用过滤器
4,利用Session防止表单重复提交
具体的做法:
在服务器端生成一个唯一的随机标识号,专业术语称为Token(令牌),同时在当前用户的Session域中保存这个Token。然后将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。
在下列情况下,服务器程序将拒绝处理用户提交的表单请求:
1,存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。
2,当前用户的Session中不存在Token(令牌)。
3,用户提交的表单数据中没有Token(令牌)。
例如:
创建FormTokenServlet,用于生成Token和跳转到token.jsp页面:
public class FormTokenServlet extends HttpServlet {
private static final long serialVersionUID = -8236507185410764108L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建令牌
String token = TokenProccessor.getInstance().makeToken();
System.out.println("在FormServlet中生成的token:" + token);
// 在服务器使用session保存token(令牌)
req.getSession().setAttribute("token", token);
// 跳转到token.jsp页面
req.getRequestDispatcher("/token.jsp").forward(req, resp);
}
}
在token.jsp中使用隐藏域来存储Token(令牌),提交Token(令牌)到服务器:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<html>
<head>
<base href="<%=basePath%>" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>表单提交</title>
<link href="css/bootstrap.css" rel="stylesheet">
<script src="js/jquery-3.2.1.js"></script>
<script src="js/bootstrap.js"></script>
</head>
<body>
<form class="form-horizontal" action="<%=request.getContextPath()%>/TokenServlet.html" role="form" method="post">
<input type="hidden" name="token" value="<%=session.getAttribute("token") %>">
<div class="form-group">
<label for="firstname" class="col-sm-1 control-label">用戶名</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="idCodeNum"
placeholder="请输入用戶名">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-3">
<button type="submit" class="btn btn-default">提交</button>
<button type="reset" class="btn btn-default">重置</button>
</div>
</div>
</form>
</body>
</html>
TokenServlet处理表单提交:
public class TokenServlet extends HttpServlet {
private static final long serialVersionUID = -8236507185410764108L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
boolean b = false;
String client_token = req.getParameter("token");
// 如果用户提交的表单数据中没有token,则用户是重复提交了表单
if (client_token == null) {
b = true;
}
// 取出存储在Session中的token
String serverToken = (String) req.getSession().getAttribute("token");
// 如果当前用户的Session中不存在Token,则用户是重复提交了表单
if (serverToken == null) {
b = true;
}
// 存储在Session中的Token与表单提交的Token不同,则用户是重复提交了表单
if (!client_token.equals(serverToken)) {
b = true;
}
if (b == true) {
System.out.println("请不要重复提交!");
return;
}
// 移除session中的token
req.getSession().removeAttribute("token");
System.out.println("正在处理用户提交请求!!");
}
}
运行结果如下:
十,用户禁用Cookie后的Session处理
这里存在一种情况,假如用户浏览器禁用了Cookie怎么办?比如我把Chrome的Cookie禁用,如下:
解决方法:URL重写
Servlet中的response提供了对URL重写的方法:
response.encodeRedirectURL(String url) |
用于对sendRedirect方法后的url地址进行重写 |
response.encodeURL(String url) |
用于对表单action和超链接的url地址进行重写 |
那么URL重写是什么意思呢?其实就是人为地把JSESSIONID附在了url的后面,比如我们修改之前写的简易购物车,ShowBook中所有的点击购买超链接都要重写。
之前我们是这么写的:
out.println("<tr><td>"+book.getName()+"</td><td><a href='"+ req.getContextPath() +"/BuyBookServlet.html?id="+book.getId()+"'>点击购买</a></td></tr>");
现在进行URL重写:
req.getSession();
String url = "/MyCart/BuyBookCl?id="+book.getId();
url = resp.encodeURL(url);
out.println("<tr><td>"+book.getName()+"</td><td><a href='"+url+"'>点击购买</a></td></tr>");
需要注意的是,重写之前一定要调用或者确保使用过request.getSession()这个方法。
重写之前,访问ShowBookServlet的源代码是这样的:
重写之后呢:
可以看到URL重写之后,jsessionid这个参数自动附在了url后面,由此,得以确保我们的Session在Cookie被禁用的情况下继续正常使用。这时候我们查看购物车的地址栏如下,可以明显看到jsessionid这个参数:
Java Web学习总结(6)Cookie/Session的更多相关文章
- Java Web学习(五)session、cookie、token
文章更新时间:2020/09/14 一.引言 动态网页兴起后,会话管理变成开发者需要考虑的一个问题,由于HTTP请求是无状态的,为了区分每个用户,此时引入了会话标识(sessionId)的概念,但是存 ...
- Python Web学习笔记之Cookie,Session,Token区别
一.Cookie,Session,Token简介 # 这三者都解决了HTTP协议无状态的问题 session ID or session token is a piece of data that i ...
- Java Web之Servlet及Cookie/Session
Servlet参考文献: 1.http://www.cnblogs.com/luoxn28/p/5460073.html 2.http://www.cnblogs.com/xdp-gacl/p/376 ...
- java web学习总结(十二) -------------------Session
一.Session简单介绍 在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下).因此,在需要保存用户数据时,服务 ...
- java web 学习十二(session)
一.Session简单介绍 在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下).因此,在需要保存用户数据时,服务 ...
- [原创]java WEB学习笔记34:Session 案例 之 解决表单重复提交
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- [原创]java WEB学习笔记33:Session 案例 之 购物车
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- java web学习总结(十三) -------------------使用Session防止表单重复提交
在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交. 一.表单重复提 ...
- java web 学习十三(使用session防止表单重复提交)
在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交. 一.表单重复提 ...
- [原创]java WEB学习笔记28: 会话与状态管理Cookie 机制
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
随机推荐
- Python的Django框架中的URL配置与松耦合
Python的Django框架中的URL配置与松耦合 用 python 处理一个文本时,想要删除其中中某一行,常规的思路是先把文件读入内存,在内存中修改后再写入源文件. 但如果要处理一个很大的文本,比 ...
- 阅读笔记06-架构师必备最全SQL优化方案(2)
四.基础优化 1.优化思路? 定位问题点吮吸:硬件-->系统-->应用-->数据库-->架构(高可用.读写分离.分库分表). 处理方向:明确优化目标.性能和安全的折中.防患未然 ...
- 企业SRC整理
0.SRCs|安全应急响应中心 - 0xsafe 1.腾讯安全应急响应中心(TSRC) 2.360安全应急响应中心 3.京东安全应急响应中心(JSRC) 4.平安集团安全应急响应中心(PSRC) 5. ...
- C#将字符串Split()成数组
string str="aaajbbbjccc";string[] sArray=str.Split('j');foreach(string i in sArray) Respon ...
- [Linux] 012 文件搜索命令
文件搜索命令:find 命令名称:find 命令所在路径:/bin/find 执行权限:所有用户 语法:find [搜索范围] [匹配条件] 功能描述:文件搜索 范例: 在目录 /etc 中查找文件 ...
- [ARC083]Collecting Balls
Description 有一个 \(n\times n\) 的矩阵,矩阵内有 \(2n\) 个球.对于 \(i \in [1,n]\) ,\((0,i) (i,0)\) 的位置各有一个启动后往右走/往 ...
- js实现千位符分隔
前几天面试做保险项目的公司,被问到了一道实现千位符分割方法的题,乍一看挺简单,但做起来最后却没给出来一个合适的解决方法.回来自己琢磨了一个还行的答案. var num = 3899000001, ar ...
- Python入门习题1.温度转换
这一节的课堂例题为: 例1.编写一个Python程序,完成摄氏度到华氏度,华氏度到摄氏度的温度转换. 解: (1)分析问题:利用程序实现温度转换,由用户输入温度值,程序给出输出结果. (2)划分边界: ...
- 洛谷 P1589 泥泞路 & 2019青岛市竞赛(贪心)
题目链接 https://www.luogu.org/problemnew/show/P1589 解题思路 用结构体存下每一段泥泞路的左端点和右端点,然后用sort根据左端点排序,采用贪心的思想,从左 ...
- Codeforces - 1176E - Cover it! - bfs
https://codeforc.es/contest/1176/problem/E 久了不写bfs了.一开始用dfs写,的确用dfs是很有问题的,一些奇怪的情况就会导致多染一些色. 注意无向图的边要 ...