cookie&session(下)
第三章 Session技术
我们学习完cookie技术之后,我们发现cookie的弊端是数据的不安全性。而在开发中对于存储的数据安全性有极高要求的则不能使用cookie技术来存储。例如登录网站之后,用户的用户名和密码就不能存储在cookie中,而需要存储在另一个会话技术session中。那么我们先了解下session的原理。
1、Session是什么
Session是服务器为每个访问这个服务器的客户端用户创建的一个容器。这个容器中存储的数据能够在多个request之间实现共享。而且,这个容器只属于当前这个用户。
2、Session是怎么标识每一个用户的
表面上,我们说Session容器是属于某一个用户。其实在会话过程中,只有客户端(浏览器)和服务器两个角色。所以,Session容器标识的不是用户,而是当前用户正在使用的浏览器。浏览器与服务器交流是通过给服务器发送请求实现的,浏览器的每次请求中如果都有一个相同的标记用来标记session。服务器就能够识别这个用户了。这个标记叫做JSESSIONID。
3、Session的作用
-
为每个访问服务器的用户创建一个存储数据的容器;
-
容器中的数据在多个请求之间共享;
-
整个会话过程中,所有的servlet都可以共享session中的数据;
-
session属于域对象,整个会话过程中,可以向其中存储数据,在其他servlet中获取数据。
-
使用范围:
1)request : 2个servlet,使用在转发。一次请求
2)servletContext:全局。整个web项目,可以读取配置文件中的数据
3)session:整个会话过程中的所有的servlet之间
servletContext > session>request
4、Session容器创建和销毁的API
获取session的API详细说明:api要求记住第一个API
4.1request.getSession()
创建session 如果session不存在,创建session,存在,获取当前session
request.getSession();
4.2HttpSession session1 = request.getSession(true);
HttpSession session1 = request.getSession(true);
//两个API效果相同 request.getSession(true); 等于 request.getSession();
创建session 如果session不存在,创建session,存在,获取当前session
4.3request.getSession(false);
request.getSession(false);
如果当前存在session 获取当前session, 不存在,不获取session,返回null
4.4session.invalidate() ;
session.invalidate()使当前session失效,即销毁当前session
4.5补充:String sessionId = session.getId();
String sessionId = session.getId(); 获取session的唯一标识id
【参考代码】
@WebServlet("/createInvaliSessionServlet")
public class CreateInvaliSessionServlet 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 如果session不存在,创建session,存在,获取当前session
HttpSession session = request.getSession();
//获取session的id
String sessionId = session.getId();
System.out.println(sessionId);//E3ED5876FF2F6D355EC6B5DA8893CEC2
//request.getSession(true); 等于 request.getSession();
HttpSession session1 = request.getSession(true);
//获取session的id
String session1Id = session1.getId();
System.out.println(session1Id);//E3ED5876FF2F6D355EC6B5DA8893CEC2
//当前session销毁。。
//这里使用session或者session1销毁都可以,因为他俩都是一样的
session.invalidate();
HttpSession session2 = request.getSession(false);//如果当前存在session 获取当前session, 不存在,不获取session
System.out.println(session2);//null
if (null== session2){
System.out.println("session没有被创建");
}
}
}
效果图:
5、Session的其他常用API(了解)
HttpSession接口方法 | 作用 |
---|---|
long getCreationTime() | 表示会话创建的时间,返回long类型。 表示1970-1-1到这个时间之间相差的毫秒数 |
boolean isNew() | 判断当前是否是一个新的会话,是的返回true |
代码演示如下:
package com.ithea.sh.web;
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;
import java.io.PrintWriter;
import java.sql.Timestamp;
@WebServlet("/sessionAPIServlet")
public class SessionAPIServlet 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 {
//得到会话对象
HttpSession session = request.getSession();
//表示会话创建的时间,返回long类型。表示1970-1-1到这个时间之间相差的毫秒数
long creationTime = session.getCreationTime();
//String s1 = new Date(creationTime).toString();
//System.out.println("s1 = " + s1);//2017-10-10
//使用Timestamp将毫秒转换为日期格式
String s = new Timestamp(creationTime).toLocaleString();//2017-10-10 15:13:44
System.out.println("会话创建时间:" + s);
//判断当前是否是一个新的会话,是的返回true
boolean aNew = session.isNew();
System.out.println("是否是新的会话:"+aNew);
}
}
6、Session域对象的API(重要)
由于session属于域对象,所以Session其他的常用API包括:往session容器中存储数据,删除数据,获取数据。
方法 | 使用示例 | 说明 |
---|---|---|
void setAttribute(String name,Object value) | session.setAttribute("loginUser",user) | 将一个对象与一个名称关联 之后存储到session中 |
Object getAttribute( String name) | session.getAttribute("loginUser") | 通过名称获取session 中的数据 |
void removeAttribute(String name) | session.removeAttribute("loginUser") | 根据指定名称删除 session中的数据 |
【练习】Session常用方法练习
需求:
- 在第一个setSessionServlet中向session中存储数据;
- 在另外一个getSessionServlet中获取session中存储的值,打印session值;
【代码一】SetSessionServlet:往session中设置值
@WebServlet("/setSessionServlet")
public class SetSessionServlet 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();
System.out.println(session.getId());
//向session中存储数据
session.setAttribute("one","test");
}
}
【代码二】GetSessionServlet:从session中取值
@WebServlet("/getSessionServlet")
public class GetSessionServlet 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();
System.out.println(session.getId());
//获取session中存储值
String one = (String) session.getAttribute("one");
System.out.println(one);
}
}
效果图:
7、Session的生命周期(了解即可)
7.1 Session创建
当客户端浏览器第一次访问服务器时,服务器为每个浏览器创建不同的HttpSession对象。在服务器端使用request.getSession()方法来获得HttpSession对象。
当第一次执行 request.getSession()是session对象就被创建了。后续的request.getSession()只能获取已创建的session。
7.2 Session销毁
方式一:tomcat服务器非正常关闭(例如电脑电源突然断电)【了解】
方式二:时间超出了session的存活时间
session的默认存活时间是30分钟,在tomcat的全局配置文件web.xml中。(路径:tomcat/config/web.xml)
我们可以在web.xml中自己设置这个存活时间。我们设置的这个时间会覆盖原来的存活时间。
【注意】
注意:30分钟从什么时候开始计算?
从用户最后一次访问时间开始计算:
Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,并维护该Session。用户每访问服务器一次,无论是否读写Session,服务器都认为该用户的Session"活跃(active)"了一次。
注意: 开发中一般我们将session设置为30分钟,所以没有必要对session的存活时间进行设置。这个知识点了解就好了。
如果想手动配置session的存活时间,可以将session-config这段配置复制到自己项目中的web.xml中。就可以修改session的存活时间了
【web.xml配置】
<?xml version="1.0" encoding="UTF-8"?>
<web-app 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_3_1.xsd"
version="3.1">
<!--将session的存活时间设置成50-->
<session-config>
<session-timeout>50</session-timeout>
</session-config>
</web-app>
提示:知识点了解即可,不需要掌握。
方式三:立即销毁
HttpSession方法 | 功能描述 |
---|---|
invalidate() | 会话立刻失效,一般用于用户退出,注销 |
代码演示:
@WebServlet("/sessionAPIServlet")
public class SessionAPIServlet 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 {
//得到会话对象
HttpSession session = request.getSession();
String sessionId = session.getId();
//立即销毁
session.invalidate();
System.out.println("sessionId = " + sessionId);
}
}
每次访问session的唯一标识sessionId都不一样。
方式四:手动设置过期时间
如何在服务器上查看session的销毁时间?
查看Session的销毁时间:
session中的方法 | 说明 |
---|---|
int getMaxInactiveInterval() | 得到服务器上会话最大的非活动时间间隔,默认是1800秒(30分钟) |
【注意】
时间间隔的含义:如果你在这段时间内再次发送请求给服务器,服务器将会重新计时。
手动设置Session的过期时间:
HttpSession的方法 | 功能描述 |
---|---|
void setMaxInactiveInterval(int 秒) | 设置会话最大非活动时间时隔,单位是秒 |
【演示】设置会话过期的时间为5秒,输出过期的时间,输出会话ID
/**
* 会话存在的时间
*/
@WebServlet("/demo4")
public class Demo4MaxAgeServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//创建会话
HttpSession session = request.getSession();
//设置过期时间,5秒
session.setMaxInactiveInterval(5);
//当前会话存活的时间是
System.out.println("会话存在的时间:" + session.getMaxInactiveInterval());
System.out.println("会话ID: " + session.getId());
}
}
7.3 浏览器关闭后,session持久化方案(重要)
通过上面的学习,我们已经知道在服务器创建session的过程中同时生成一个jsessionid和一个cookie,将jsessionid存储到cookie中,然后响应到浏览器中,当下次访问服务器的时候会将cookie中的jsessionid取出到服务器中找对应的session。那么现在会有2个问题,cookie属于什么级别的呢?如果cookie消失,还可不可以找到对应的session?
答案:首先服务器创建的cookie属于会话级别的cookie,即把浏览器关闭,cookie就会消失,由于jsessionid存储在cookie中,也会随着消失,下次打开浏览器在访问服务器的时候就找不到对应的session了,服务器又会重新创建一个新的session容器。那么如果多次创建session并且多次关闭浏览器就会在服务器端产生多个无用的session,会占服务器的资源,服务器性能降低。
补充:session在服务器中的存活时间默认是30分钟。30分钟之后才可以消失。不是浏览器关闭了就消失。
上述问题的代码演示如下:
@WebServlet("/persistenceServlet")
public class PersistenceServlet 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并获取session的id
String id = request.getSession().getId();
//输出id
System.out.println(id);
}
}
idea控制台结果:
说明:在浏览器访问上述servlet,然后关闭浏览器,然后再次访问servlet,连续重复多次,发现每次session的id都不一样,说明了浏览器关闭,就找不到之前的session了,重新请求就会重新创建一个新的session。
【解决问题】:
我们使用session持久化方案可以解决 上述问题。我们只需要把会话级别的cookie变为持久化级别的cookie,那么就可以解决了。建议让cookie存活时间也是30分钟,和session存活时间一致。
【实现方案】:
1. 在Servlet中创建session并获取JSESSIONID;
2. 创建Cookie对象,将上述从session中获取的JSESSIONID放到cookie中;
3. 手动设置存放JESSIONID的cookie的存活时间为30分钟;
4. 将存放JESSIONID的cookie响应给浏览器;
原理图:
【参考代码】
@WebServlet("/persistenceServlet")
public class PersistenceServlet 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持久化操作
//创建session并获取session的id
String id = request.getSession().getId();
/*
将session的id放到cookie中,注意JSESSIONID是session的id名字
*/
Cookie cookie = new Cookie("JSESSIONID",id);
//输出id
System.out.println("id = " + id);
//对cookie进行持久化操作 存活30分钟
cookie.setMaxAge(60*30);
//cookie响应回浏览器
response.addCookie(cookie);
}
}
【运行结果】
关闭浏览器再次访问:http://localhost:8080/persistenceServlet
总结:因为cookie被我们持久化了,所以,即使用户再次关闭浏览器,都会一直访问到刚才的session。不会再次创建新的session而消耗内存了。
session持久化很重要,要求大家掌握
8、Session的钝化与活化技术(了解)
目标
了解服务器会话的钝化和活化技术
提问
判断下面的说法是否正确?
-
浏览器关闭的时候,服务器端的session就销毁?
不会。session存在服务器端,和浏览器没有关系。
-
服务器关闭的时候,服务端的会话就销毁?
1)tomcat正常关闭:session会被钝化===》work\Catalina\localhost\项目名
2)tomcat非正常关闭:session会销毁
概念
在正常的情况下,如果服务器关闭或重启,服务器会将所有session域中数据(对象)以文件的方式保存到服务器的硬盘上,下次服务器重新启动的时候,读取硬盘上文件,把这些对象还原。
回顾
package com.ithea.sh.servletcontext_03;
import java.io.Serializable;
public class Product implements Serializable {
private String name;
private Integer price;
public Product(String name, Integer price) {
this.name = name;
this.price = price;
}
public Product() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
@Override
public String toString() {
return "Product{" +
"name='" + name + ''' +
", price=" + price +
'}';
}
}
- 序列化: 将内存中对象以文件的方式保存
- 反序列化: 读取文件,还原成对象
public class SerializeTest {
@Test
public void testWrite( ) throws Exception {
//对象序列化:对象==》文件保存在本地
Product product = new Product("宝马",122);
//1、创建文件输出流
FileOutputStream outputStream = new FileOutputStream("product.txt");
//2、创建对象流
ObjectOutputStream oos = new ObjectOutputStream(outputStream);
//3、写出文件
oos.writeObject(product);
oos.close();
}
/**
* 测试反序列化
*/
@Test
public void testRead( ) throws Exception {
//1、创建文件输入流
FileInputStream inputStream = new FileInputStream("product.txt");
//2、创建对象输入流
ObjectInputStream ois = new ObjectInputStream(inputStream);
//3、将product.bin文件转换成 Product对象
Product product = (Product) ois.readObject();
System.out.println("product = " + product);
}
}
会话的钝化
钝化:将session中所有的对象写入到服务器硬盘文件上
注:项目要部署在tomcat中才有效果,在idea中没有效果.必须将当前项目打成war包放到安装tomcat时webapps路径下,然后到bin目录下启动tomcat(startup.bat)和关闭tomcat(shutdown.bat),然后就会在如下路径下生成一个session钝化的一个文件:
在work目录下:work\Catalina\localhost\项目名\SESSIONS.ser
会话的活化
活化:将服务器硬盘上文件读取出来,还原成会话对象。
以上过程由tomcat完成,程序员不用理会。要保存的对象必须实现序列化的接口。
步骤
- 如果一个会话中的对象要正确的钝化,必须要实现序列化的接口
- 向会话域中保存Product对象
- 在另一个Servlet中取出Product对象输出看有没有数据
- 在tomcat中**==正常关闭服务器==**,观察work目录下是否有序列化的文件
- 重启tomcat服务器,观察work目录下变化
- 在浏览器没有关闭的情况下再读取会话域中的数据,看能够读取出来
注:项目要部署在tomcat中才有效果,在idea中没有效果.必须将当前项目打成war包放到安装tomcat时webapps路径下,然后到bin目录下启动tomcat(startup.bat)和关闭tomcat(shutdown.bat),然后就会在如下路径下生成一个session钝化的一个文件:
代码演示:
【Session创建】
@WebServlet("/setSession")
public class SetSessionServlet 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 {
response.setContentType("text/html;charset=utf-8");
//1、创建session
HttpSession session = request.getSession();
//2、将对象存储到session中
Product product = new Product("奥迪", 133);
session.setAttribute("product", product);
response.getWriter().print("Session创建成功,sessionId = " + session.getId());
}
}
【Session获取】
@WebServlet("/getSession")
public class GetSessionServlet 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 {
//1、接收请求数据
HttpSession session = request.getSession();
//2、处理请求
Product product = (Product) session.getAttribute("product");
//System.out.println("从Session中获取到数据:product = " + product);
//3、响应数据
response.setContentType("text/html;charset=utf-8");
response.getWriter().print(product.toString()+"----"+session.getId());
}
}
将项目打成war包:
1.点击idea中的项目结构:在idea的右上角。
2.进入目录结构按照如下操作:
3.选择Build工程
5.然后按照如下操作:
6.将上述打好包的工程放到tomcat的安装webapps目录下:
7.到tomcat的bin目录下去启动tomcat
8.在浏览器分别访问setSession和getSession
9.正常关闭tomcat:到tomcat的bin目录下去关闭。
我们就会在work\Catalina\localhost\day10发下session钝化后的文件:
10.再次启动tomcat服务器,然后在浏览器中访问setSession和getSession
小结
-
什么是session的钝化与活化?
序列化和反序列化
-
钝化的对象需要实现哪个接口?
java.io.Serializable
9、Cookie禁用后Session的处理【了解】
目标
- Cookie禁用后重定向的处理
- Cookie禁用后超连接的处理
禁用Cookie后会话中共享数据导致的问题
- 把浏览器的Cookie禁用
- 在一个Servlet1中向session中添加一个数据,重定向到Servlet2。
- 在另一个Servlet2中的session中读取数据并显示出来
- 无法得到原来会话中的信息
禁用cookie
说明:由于cookie被禁用了,那么浏览器每次访问服务器都会重新创建一个session,并且也不会将sessionid保存到浏览器端了,浏览器中不会有任何关于cookie的信息了。那么请求的时候也不会携带cookie到服务器中了,那么这样会导致每次向服务器发送请求都会生成一个新的session,导致之前的session失效了。那么如果用户禁用浏览器中的cookie,我们应该如何解决上述这种情况呢,保证session不失效,可以一直使用呢?
可以使用两种解决方案:1)重定向重写URL 2)超连接重写URL
说明:
-
什么是URL重写:
通过地址栏将会话ID带回给服务器
-
处理机制:
首先会判断Cookie是否可用(如果cookie被禁用,那么获取不到cookie,是null),如果可用,则URL重写不起作用。如果Cookie不可用,则使用URL重写的功能。
将URL(访问地址)进行重写,在原来的URL基础上,添加当前会话的ID。
解决方案:
【1】重定向的解决方法
- 重定向的解决方法:
方法 | 说明 |
---|---|
response.encodeRedirectURL(path) | 将重定向跳转的地址进行重写,添加一个会话ID在后面。 |
重写前的URL:
demo3
重写后的URL:
demo3;jsessionid=B28EAE0FBCDEBE675060BDC9E3FF9ABD
没有重写URL代码
@WebServlet("/demo2")
public class Demo2SetServlet 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 {
response.setContentType("text/html;charset=utf-8");
//得到会话(会话域)
HttpSession session = request.getSession();
//放会话域中存入数据
session.setAttribute("product",new Product("手机",888));
response.sendRedirect("/demo3");
}
}
@WebServlet("/demo3")
public class Demo3GetServlet 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 {
response.setContentType("text/html;charset=utf-8");
//得到会话(会话域)
HttpSession session = request.getSession();
Product product = (Product) session.getAttribute("product");
response.getWriter().print(product);
}
}
效果:
说明:如果没有重写URL,我们发现重定向之后没有获取到商品信息。
重写URL代码
@WebServlet("/demo2")
public class Demo2SetServlet 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 {
response.setContentType("text/html;charset=utf-8");
//得到会话(会话域)
HttpSession session = request.getSession();
String url = "/demo3";
//放会话域中存入数据
session.setAttribute("product",new Product("手机",888));
//使用URL重写
String demo3 = response.encodeRedirectURL(url);
System.out.println(demo3);
//demo3就是重写的URL
response.sendRedirect(demo3);
}
}
@WebServlet("/demo3")
public class Demo3GetServlet 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 {
response.setContentType("text/html;charset=utf-8");
//得到会话(会话域)
HttpSession session = request.getSession();
Product product = (Product) session.getAttribute("product");
response.getWriter().print(product);
}
}
效果:
【2】超链接的解决方法
方法 | 说明 |
---|---|
response.encodeURL(path) | 对要跳转到的地址使用URL重写 |
步骤
- 在servlet1中输出一个链接,跳转到servlet2中。
- 使用上面的方法对href的地址进行URL重写,再设置href属性
- 查看跳转的链接地址
代码
@WebServlet("/demo2")
public class Demo2SetServlet 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 {
response.setContentType("text/html;charset=utf-8");
//得到会话(会话域)
HttpSession session = request.getSession();
String url = "/demo3";
//放会话域中存入数据
session.setAttribute("product",new Product("手机",888));
//使用URL重写
String demo3 = response.encodeRedirectURL(url);
System.out.println(demo3);
//demo3就是重写的URL
// response.sendRedirect(demo3);
String encodeURL = response.encodeURL(url);
response.getWriter().print("<a href='" + encodeURL + "'>取出商品</a>");
}
}
@WebServlet("/demo3")
public class Demo3GetServlet 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 {
response.setContentType("text/html;charset=utf-8");
//得到会话(会话域)
HttpSession session = request.getSession();
Product product = (Product) session.getAttribute("product");
response.getWriter().print(product);
}
}
两个方法的区别
-
共同点:都在URL后面添加了会话ID,实现了URL的重写
/demo3;jsessionid=9F713440C9DA145D99F4E1C358E402C1
-
不同点:
- encodeURL方法会修改不正确的URL地址,修改完成以后再添加JSESSIONID
- encodeRedirectURL如果路径是错误的则什么都不做
小结
方法 | 说明 |
---|---|
response.encodeRedirectURL(path) | 重定向中使用 |
response.encodeURL(path) | 超链接的使用 |
10、Session综合案例(重要)
验证码校验案例
10.1 验证码校验
10.1.1【验证码】
验证码是由服务器给浏览器端生成的一张图片,这张图片上有扭曲变形的数字或文字。主要是用来防止恶意注册或者登陆的。
10.1.2【验证码校验流程】
10.1.3【登录页面login.html】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h2>登录页面</h2>
<form action="/loginCheckCodeServlet" method="post">
账号<input type="text" name="username"><br>
密码<input type="password" name="password"><br>
验证码<input type="text" name="code">
<!--页面一加载就向checkCodeServlet发送请求-->
<img src="/checkCodeServlet" alt="验证码" style="cursor: pointer"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
【页面效果】:
10.1.4【生成验证码的servlet】CheckCodeServlet
说明:生成的验证码使用java中的GUI来完成的。了解即可,做案例的时候只需将下面的代码复制到CheckCodeServlet中即可。
/*
说明:验证码是一张图片,而这个图片中的内容是使用代码生成的。
分析和步骤:
1)创建一个可以存放图片的缓冲区BufferedImage作为画布;
2)通过画布获取到针对这个画布的画笔;
3)修改画布的背景颜色为白色;
4)设置画布的边框,画边框的时候需要注意下,如果这里写画布的宽width和高height ,就会超出画布就会看不见,所以width和height 分别-1;
5)创建一个获取随机数的对象;
6)给画布上写数据;
7)给画布上画干扰线;
8)需要把画布中的内容响应给浏览器;ImageIO.write(bi,"JPG",response.getOutputStream());
*/
@WebServlet("/checkCodeServlet")
public class CheckCodeServlet 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 {
//定义画布的宽和高
int width=120;
int height=30;
//创建一个可以存放图片的缓冲区,作为画布
//BufferedImage.TYPE_INT_RGB 表示生成图片的类型
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//通过画布获取到针对这个画布的画笔
Graphics g = bi.getGraphics();
//修改画布的背景颜色 每次使用画笔的时候都得给画笔指定颜色
g.setColor(Color.WHITE);
//填充画布
g.fillRect(0, 0, width, height);
//设置画布的边框
//给画笔指定颜色
g.setColor(Color.RED);
//给画布画边框 如果这里写width height 就会超过画布,因为边框也会占一个像素,所以这里宽和高都需要-1
g.drawRect(0, 0, width-1, height-1);
//创建一个获取随机数的对象
Random r = new Random();
//给画布上画干扰线
//循环控制画多条线
for(int i=1;i<=3;i++)
{
//设置画笔的颜色
g.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
//向画布上画干扰线
//drawLine(x1, y1, x2, y2) 这里四个参数是因为两个点画成一条线
g.drawLine(r.nextInt(width), r.nextInt(height), r.nextInt(width), r.nextInt(height));
}
//定义数据准备向画布中写数据
String data="abcdefghigklmnpqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ123456789";
//创建字符串缓冲区
StringBuilder sb = new StringBuilder();
//循环控制画四个字符
for (int i = 1; i <=4; i++) {
//设置画笔的颜色 Color.BLUE这里的颜色固定了,只能是蓝色,我们可以让颜色随机变化
// g.setColor(Color.BLUE);
g.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
//设置字体 Font.ITALIC表示斜体
g.setFont(new Font("宋体", Font.ITALIC, 20));
//给画布上写内容 20表示从x轴的位置开始书写 25表示y轴位置开始书写
// g.drawString("哈哈哈哈", 20, 25);
/*
* data.charAt()表示根据函数的参数进行查找字符
* data.length()表示字符串的长度
* r.nextInt()表示生成随机数,但是随机数的范围在0~data字符串的长度
*/
String str = data.charAt(r.nextInt(data.length())) + "";
g.drawString(str, 20*i, 25);
//将验证码内容拼接到字符串缓冲区中
sb.append(str);
}
// 验证码保存到session中
request.getSession().setAttribute("checkcode",sb.toString());
//将生成的验证码图片响应给浏览器
ImageIO.write(bi,"JPG",response.getOutputStream());
}
}
注意:上述虽然实现了登录页面加载的时候获取到验证码的图片,但是我们每次点击图片,图片并没有发生改变,我们希望当验证码图片看不清楚的时候可以再次向CheckCodeServlet中发送请求并获取一张新的验证码图片,所以在登录页面给验证码图片添加一个点击的js事件
给验证码图片的img标签添加点击事件。
<img src="/checkCodeServlet" alt="验证码" style="cursor: pointer" onclick="changeCheckCode(this);"><br>
添加完成之后,书写js代码:
<script type="text/javascript">
//自定义函数向checkCodeServlet发送请求获取验证码图片
function changeCheckCode(obj)
{
alert("锁哥");
obj.src = "/checkCodeServlet";
}
</script>
说明:但是当我们点击验证码图片,函数changeCheckCode被执行了,但是图片没有改变。原因是在浏览器第一次加载图片之后将图片放到浏览器缓存中了,而以后再点击图片发送请求的时候页面不会到changeCheckCode中获取验证码图片,而是从浏览器缓存中获取,因为浏览器会认为你每次点击图片都是原来的请求,而我们希望每次点击图片都是一个新的请求。只有这样浏览器才会将新的请求发送给服务器。而不是从浏览器缓存中获取验证码图片。
做法:我们可以先看下其他网站怎么做的,例如网易的163,网址:www.163.com.访问到注册邮箱的页面,可以使用f12抓包技术来查看。
说明:点击一次验证码,都会抓住一次请求,发现每次后面的参数都不一样,这里使用的是系统时间,因为系统时间一直在变化。这样每次点击图片的时候都是发送新的请求,而不会再从浏览器缓存中获取验证码了。
给js函数中的请求路径添加系统时间,由于是get请求 ,直接使用?拼接参数。
【给验证码添加单击事件】每次点击后重新加载
<script type="text/javascript">
//自定义函数向checkCodeServlet发送请求获取验证码图片
function changeCheckCode(obj)
{
// alert("锁哥");
obj.src = "/checkCodeServlet?"+new Date().getTime();//new Date().getTime() 表示将系统时间变为毫秒
}
</script>
效果图:点击img标签,验证码会发生变化,不从缓存中获取数据,直接走新的请求。
【完整页面】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h2>登录页面</h2>
<form action="/loginCheckCodeServlet" method="post">
账号<input type="text" name="username"><br>
密码<input type="password" name="password"><br>
验证码<input type="text" name="code">
<!--页面一加载就向checkCodeServlet发送请求-->
<img src="/checkCodeServlet" alt="验证码" style="cursor: pointer" onclick="changeCheckCode(this);"><br>
<input type="submit" value="登录">
</form>
<script type="text/javascript">
//自定义函数向checkCodeServlet发送请求获取验证码图片
function changeCheckCode(obj)
{
// alert("锁哥");
obj.src = "/checkCodeServlet?"+new Date().getTime();//new Date().getTime() 表示将系统时间变为毫秒
}
</script>
</body>
</html>
10.1.5【校验验证码是否输入正确】
@WebServlet("/loginCheckCodeServlet")
public class CheckServlet 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 username = request.getParameter("username");
String password = request.getParameter("password");
//获取用户输入的验证码
String code = request.getParameter("code");
/*
从session中获取验证码 ,在生成验证码的servlet中CheckCodeServlet书写这样的代码:
request.getSession().setAttribute("checkcode",sb.toString());
表示将验证码已经放到session中了,所以我们要获取出来
*/
String checkcode = (String) request.getSession().getAttribute("checkcode");
//判断用户输入的验证码code和随机生成的验证码checkcode是否一致,忽略大小写
if(checkcode.equalsIgnoreCase(code))
{
System.out.println("验证码正确");
}else
{
System.out.println("验证码错误");
}
}
}
11、Servlet作用域总结
-
ServletContext域:
一个WEB应用(项目)对应一个ServletContext,这个对象中保存的数据在整个WEB项目中都有效;
- 创建:服务器启动的时候;
- 销毁:服务器关闭或项目移除后;
-
HttpSession:
一次会话的过程中,服务器给客户端(浏览器)创建一个session。这个对象中保存的数据,一次会话(多次请求)内数据有效;
-
创建:服务器第一次调用getSession()的时候;
-
销毁:
- 服务器不正常关闭;
- Session过期了:默认存活时间30分钟;
- 手动调用session的invalidate()方法;
-
-
HttpServletRequest:
一次请求创建一个request。这个对象中保存的数据,一次请求(请求链)内数据有效;
- 创建:客户端向服务器发送一次请求;
- 销毁:服务器为这次请求做出响应之后,销毁request;
【API操作】操作三个作用域对象的API
-
存储数据:setAttribute(name,value);
-
获得数据:getAttribute(name);
-
删除数据:removeAttribute(name);
大小:HttpServletRequest<HttpSession<ServletContext
第四章 小结
转载自:https://juejin.cn/post/7241020073481650232