一.状态管理
1.状态管理过程
将客户端和服务器端多次交互当做一个整体来看待,并且将多次交互涉及的数据保存下来,提供给后续交互,进行数据的管理就是状态管理。
2.剖析
状态是指当前的数据
管理是指多次交互过程中对数据的存储,修改和删除。
3.例子:
1.洗车卡记录洗车次数,由商家修改卡里的次数,商家不保存任何数据,客户来携带需要维护的数据
2.商家给客户卡号(标识),客户报上卡号,商家通过卡号找到对应的记录来修改记录,由商家保存数据
4.程序中的状态管理
分为Cookie和Session
Cookie——>针对的网站,数据存在客户端。
Session——>针对的是用户,数据存在服务器端。
二.Cookie会话
1.简介
只能保存一小段字符串类型的文本信息(不能保存复杂的对象类型数据),随着请求和响应,在客户端和服务器端之前来回传递,根据设定的时间来决定该段文本在客户端保存时长的工作模式叫Cookie会话
2.原理
1.客户端向服务器端的AddServlet发送请求,遇到创建cookie的代码时,一小段的文本信息就会以键值对(name=value)的形式,随着response响应中的头信息被传递并保存到客户端。
2.当文本信息到达客户端,文本信息会被保存到客户端的内存或者硬盘上,存在内存中会随着内存的释放而消失,存在硬盘中会保存更长的时间。
3.客户端存有服务器发回的文本信息,浏览器再向服务器发起请求,如果请求到了FindServlet,那么客户端存储的文本信息会随着请求数据包以键值对的形式将文本信息发送到服务器端
4.只要cookie的生命周期没有结束,不管是存在内存还是硬盘上的信息都会在客户端向服务器端发出请求时自动的随着消息头发送过来。
5.浏览器关闭Cookie也会随着消失。
3.创建添加Cookie
Cookie c=new Cookie(String name,String value);
response.addCookie(c);
4.查询Cookie
获取客户端所有的Cookie对象
Cookie[] cookies= request.getCookes();
5.获取一个Cookie对象的名称或值
String name=Cookie.getName();
String value=Cookie.getValue();
5.修改Cookie值
Cookie.setValue(String newValue);
6.Cookie值的特点
①.只能保存字符串,而且不能是中文,不能保存复杂对象类型
②.只能保存少量数据,长度有限制,不能太长,4kb左右
③.Cookie可以被禁止—在浏览器—高级—网站设置—Cookie和网站数据—阻止第三方Cookie
⑤.传输过程中,安全性很低,而且会加大浏览器的负载量
7.解决Cookie乱码问题
解决创建Cookie的乱码:String encod=URLEncoder.encode (“张三”, “utf-8”);
解决获取Cookie的乱码:String value =URLDecoder.decode (cookie.getValue (), “utf-8”);
8.创建cookie后设置Cookie的有效路径
Cookie.setPath(String path);
path是有效路径,只有在浏览器访问当前路径或当前子路径,浏览器才会发送cookie
理解有效路径指:当客户端向http://localhost:8090/test/file/addcookie. jsp发送请求时创建了Cookie,那么该 Cookie的路径就是/test/file.请求/test/file/d.jsp会发送Cookie,请求/test/d.jsp不会发送Cookie
9.Cookie生存时间
设置Cookie的过期时间使用如下代码:
setMaxAge(int seconds);(秒)
seconds > 0 :代表Cookie保存在硬盘上的时长
seconds =0 :立即删除
seconds <0 :缺省值(默认值),浏览嚣会将Cookie保存在内存中,浏览器关闭则Cookie消失
默认Cookie会被浏览器保存在内存中,Cookie的生命周期由浏览器决定,不关闭浏览器Cookie就会一直存在。
如希望关闭浏览器后Cookie仍存在,则可以通过设置过期时间使得Cookie存在硬盘上得以保存更长的时间。
10.练习
//创建cookie_Session的Web项目,创建web文件夹,创建AddCookieServlet.java的Servlet文件
package cookie;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AddCookieServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//创建cookie
//解决创建Cookie的乱码
String encod=URLEncoder.encode ("张三", "utf-8")
Cookie c1=new Cookie("uname",encode);
Cookie c2=new Cookie("city","zz");
//设置c1的过期时间,c2没有设置,就是在浏览器关闭时就过期了
c1.setMaxAge(100);
//设置cookie可发送路径
//本来用http://localhost:8080/cookie_session/findCookie可以访问
//但是设置了c1.setPath("/test");
//c1只能用http://localhost:8080/cookie_session/test/findCookie来访问
c1.setPath("/test");
//添加Cookie至Response
response.addCookie(c1);
response.addCookie(c2);
}
}
//创建FindCookieServlet.java的Servlet文件
package cookie;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FindCookieServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
Cookie[] cookies = request.getCookies();
if(cookies!=null){
for(Cookie cookie:cookies){
//解决获取Cookie的乱码
String value =URLDecoder.decode (cookie.getValue (), "utf-8");
String value = cookie.getValue();
out.println("name:"+name+",value:"+value+",");
}
}else{
out.println("没有Cookie信息");
}
}
}
//创建UpdateCookieServlet.java的Servlet文件
package cookie;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class updateCookieServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
if(cookies!=null){
for(Cookie cookie:cookies){
if(cookie.getName().equals("city")){
//重新设置中文编码
String encode1 =URLDecoder.decode ("上海", "utf-8");
//修改Cookie,一定不能设置为中文
cookie.setValue(encode1 );
//cookie.setValue("sh");
//保存到response中
response.addCookie(cookie);
}
}
}
}
}
三.Session会话
1.简介
在服务器端按照客户端提供的编号找到对应的数据进行管理叫Session会话,sessionid会根据一次会话存储在客户端浏览器的内存中
2.原理
1.当浏览器第一次访问服务器时,服务器会为不同的客户端在内存中分配一块内存空间,并且由SID的形式标识着这块内存空间,创建用于保存数据的Session对象,并且由服务器端保存。
2.服务器会将标识该对象的唯一SID随着响应发回给该对象对应的客户端。
3.当同一个客户端再次发送请求时,不同的客户会带回自己保存的SID标识,发送到服务器端
4.服务器凭借这个唯一的SID找到对应的Session对象
3.session的工作原理
服务器端先创建好,给浏览器返回一个sessionid,当浏览器再次访问session时,如果有id就会携带到服务器端,服务器端如果找到了对应的sessionid,就会继续用这个sessionid,如果没找到就会新创建一个。
4.获得Session
HttpSession s=request.getSession(boolean flag);//若flag为true:没有sid创建session,false:没有返回null
HttpSession s=request.getSession();//默认为true,无论是否带sessionid都会返回一个session的,要么是找到的,要么是创建的session对象
5.使用session绑定对象
void session.setAttribute(String name,Object obj)
session可以绑定对象,而cookie只能绑定字符串
6.获取绑定数据和移除绑定数据
void session.getAttribute(String name);
void session.removeAttribute(String name);
7.删除session对象
void session.invalidate()
8.URL重写
定义:浏览器访问服务器的某地址时,会使用一个该写过的地址,即在原有地址后追加sessionid,这种重新定义url内容的方式叫url重写。
使用原因:在浏览器的设置,高级设置里面第三方禁用cookie,cookie禁用情况下,session是不能保存id的,使用URL重写就可以保存sessionid
URL重写的方法
response.encodeURL(String url):生成链接地址和表单提交
response.encodeRedirectURL(String url):重定向
9.session的生命周期(超时时间)
session对象存在于内存中时会有默认的时间限制,超过缺省时间,session失效不能继续访问Web服务器,缺省的超时时间设置一般是30分钟
修改session的缺省时间限制
1.编程式针对该方法的session对象:void setMaxInactiveInterval(int seconds)(秒)
2.声明式设置使用session对象:在tomcat的conf目录下,寻找web.xml文件修改为30分钟
<session-config><session-timeout>30</session-timeout></session-config>
10.验证码
本质:一张图片,图片内容会随着程序的运行而随机生成
作用:防止应用恶意发送数据,一定程度上避免了恶意程序对网站的攻击
验证码本质上是一张图片,图片内容的准确解析不容易用程序来实现
绘制:绘制验证码图片不仅仅需要随机生成要绘制的内容,同时要配合Java中与绘图有关的一套API来完成
11.session的优缺点
优点
安全(将状态保存在服务器端)
Session能够保存的数据类型更丰富, Cookie只能保存字符串
Session能够保存更多的数据, Cookie大约保存4k
缺点
Session将状态保存在服务器端,占用服务器的内存,如果用户量过大,会严重影响服务器的性能
12.练习
1.记录用户访问次数
通过访问http://localhost:8080/cookie_session/countServlet,f12—Network—Cookie: JSESSIONID=5097BB7D99CD7DC1E06ECEEE4399027C; uname=zs; city=zz—浏览器的request Headers的JSESSIONID中存储着sessionid,浏览器没有关闭,sessionid就一直存在,浏览器关闭重新访问,就会在浏览器重新创建一个sessionid,在response Headers中会出现新创建的sessionid
package session;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class CountServlet extends HttpServlet {
//记录用户访问的次数
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//设置输出编码
response.setContentType("text/html;charset=utf-8");
//设置输出流
PrintWriter out = response.getWriter();
//获得session对象
HttpSession session=request.getSession();
//输出自动分配的sessionid
System.out.println(session.getId());
//获取绑定的计算器,绑定count到sessionid,先看看count有没有值
Integer count=(Integer)session.getAttribute("count");
//count=null,说明是第一次访问,给count=1;不为空说明不是第一次访问,就count++
if(count==null){
count=1;
}else{
count++;
}
//将最新的count值同步到session中
session.setAttribute("count", count);
//在浏览器中输出当前访问次数
out.println("这是第"+count+"访问");
//关闭流
out.close();
}
}
2.实现一个登录,登录成功之后可以访问index.jsp页面,没有登录成功不能访问
实现过程:登录信息提交后,后台开始验证,验证通过(用户名,密码,验证码),将用户信息保存到session中,验证失败,就转发到登录页面,重新登录;
对于需要保护的资源,添加从session中获取绑定值的功能来判断是否能够成功获取绑定的用户信息,成功获取到,则可以访问被保护页面,未成功获取,就拒绝访问该资源,并且重定向到登录页面,重新登录。
登录成功让访问index.jsp界面,不成功不让访问
login.jsp:登录界面,用户名,密码,验证码
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
Object msg=request.getAttribute("msg");
if(msg!=null){
%>
<%=msg.toString()%>
<%}%>
<html>
<head>
<style type="text/css">
.s1{
cursor:pointer;
}
</style>
</head>
<body>
<form action="login.do" method="post">
用户名:<input name="uname"/><br/>
密码:<input name="pwd" type="password"/></br>
验证码:<input name="vcode"/>
<img src="code" οnclick="this.src='code?'+Math.random();" class="s1" title="点击更换"/><br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
index.jsp:登录成功后可访问的界面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
//session验证
Object uname=session.getAttribute("uname");
if(uname==null){
//重定向到login.in中
response.sendRedirect("login.jsp");
return;
}
%>
<html>
<head>
</head>
<body>
<h1>欢迎你:<%=uname.toString()%></h1>
<!-- 登录之后,点击超链接的登出,就可以返回登录界面 -->
<a href="logout.do">登出</a>
</body>
</html>
CodeServlet.java:随机生成验证码,并且将五位验证码数字存储到session中
package session;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class CodeServlet extends HttpServlet {
//验证码的servlet
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//1.创建空白图片
BufferedImage image=new BufferedImage(100,30,BufferedImage.TYPE_INT_RGB);
//2.获取图片笔画
Graphics g=image.getGraphics();
Random r=new Random();
//3.设置笔画颜色
g.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
//4.绘制矩形背景
g.fillRect(0, 0, 100, 30);
//5.调用自定义方法,获取字母长度为5的字母组合的字符串
String number=getNumber(5);
//6.获得session对象,将验证码五位随机字符串存入session
HttpSession session = request.getSession();
session.setAttribute("code",number);
//7.设置session失效时间为10s
session.setMaxInactiveInterval(30);
//8.设置字体颜色
g.setColor(new Color(0,0,0));
g.setFont(new Font(null,Font.BOLD,24));
//9.绘制字符串
g.drawString(number, 5, 25);
//10.绘制8条干扰线
for(int i=0;i<8;i++){
g.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255),r.nextInt(255)));
g.drawLine(r.nextInt(100), r.nextInt(30), r.nextInt(100),r.nextInt(30));
}
response.setContentType("image/jpeg");
OutputStream ops =response.getOutputStream();
ImageIO.write(image, "jpeg", ops);
ops.close();
}
private String getNumber(int size){
String str="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
String number="";
Random r=new Random();
for(int i=0;i<size;i++){
number+=str.charAt(r.nextInt(str.length()));
}
return number;
}
}
ActionServlet.java:判断登录界面的密码,用户名,验证码是否正确,正确就重定向到index.jsp中,不正确,就重新转发到login.jsp登录界面,重新登录
package session;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class ActionServlet extends HttpServlet {
//在这个合并servlet中,通过请求路径,来判断请求的是哪个servlet
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
HttpSession session=request.getSession();
//设置编码
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//设置session的超时时间
session.setMaxInactiveInterval(10);
PrintWriter out = response.getWriter();
//获得请求路径
String uri=request.getRequestURI();
//拆分路径,只保留login.d中的login
String url=uri.substring(uri.lastIndexOf("/")+1,uri.lastIndexOf("."));
//判断请求路径是否为登录
if(url.equals("login")){
String uname=request.getParameter("uname");
String pwd=request.getParameter("pwd");
//获得用户提交的验证码
String vcode =request.getParameter("vcode");
//获得session存储的最新验证码字符
String code = session.getAttribute("code").toString();
if(uname.equals("111")&&pwd.equals("111")&&code.equals(vcode)){
//如果用户名和密码都为111,登录通过
//在浏览器session中绑定当前登录的用户(只保留用户名即可),也可以创建一个实体类,保存这个用户(包括用户名和密码),因为session的类型是Object类型
session.setAttribute("uname",uname);
//重定向到index.jsp页面中
//response.sendRedirect("index.jsp");
//如果禁用cookie,就可以使用url重写
response.sendRedirect(response.encodeRedirectURL("index.jsp"));
}else{
//登录不通过
//绑定一个错误信息
request.setAttribute("msg", "用户名密码或验证码输入有误");
//转发到登录页面
request.getRequestDispatcher("login.jsp").forward(request,response);
}
}else if(url.equals("logout")){
//使session失效
session.invalidate();
//重定向到登录页面
response.sendRedirect("login.jsp");
}
}
}