基于Cookie与Session的Servlet API会话管理操作
目录
- ️前面的话️
- 1.Cookie与Session
- 1.1回顾与理解Cookie与Session
- 1.2Servlet会话管理操作
- 2.常见案例实现
- 2.1登录逻辑的实现
- 2.2上传文件
️前面的话️
本篇文章将介绍Servlet中的会话管理操作,前面在介绍HTTP的时候,我们已经说了Cookie与Session的基本原理,Cookie相当于就诊卡,就诊卡一刷得到的信息就相当于Session,Servlet对Cookie与Session有着很好的支持,可以实现会话的管理操作。
1.Cookie与Session
1.1回顾与理解Cookie与Session
通过学习HTTP,我们知道HTTP协议是“无状态”协议,这里的“无状态”指的是默认情况下 HTTP 协议的客户端和服务器之间的当次通信, 和下次通信之间没有直接的联系,但是在实际开发中是需要建立起来联系的,比如一个网站的登录,在生活中,我们登录一次网站后,再退出网站后,再次登录网站,我们会发现并不需要再次输入账号密码登录,网站会自动地帮助我们登录。
为了实现类似这种网站的自动登录,在用户第一次输入账号密码登录的时候,服务器会创建一个Session会话来保存当前用户的数据和信息,和生成一个Cookie,Cookie里面含有该Session里面的一些关键身份信息,服务器会将这个Cookie作为响应给客户端,客户端或者说是浏览器就会将这个Cookie储存起来,当下一次登录时,浏览器发送请求的时候就会带上这个Cookie,服务器收到请求后会去获取请求中的Cookie列表,并去查询服务器中是否存在对应的Session,如果存在就会自动登录,不用用户输入账号与密码,否则需要用户输入账号密码进行登录。
打个比方来说,去医院看病需要先挂号,如果你没有当前医院的就诊卡,就会为你新办理一个就诊卡,这个就诊卡里面含有你的一些关键身份信息,并且会在医院的服务器上新建一个档案,得到这个就诊卡,你就可以在该医院的各个科室进行刷卡,如果你之前在这个医院有就诊记录,你一刷卡就可以查询到你所有在当前医院的就诊信息。 上面的这个就诊卡就相当于这里的Cookie,上面有你最基本的身份信息,在医院服务器上所储存有关你的详细信息,就相当于一个会话,也就是一个Session,当然服务器上不止你一个人的Session,它包含很多用户的信息。
这个Cookie与Session机制最主要的作用就是用来识别用户的身份信息。
1.2Servlet会话管理操作
在HttpServletRequest
类中,可以使用getSession
来获取或创建会话与getCookies
可以获取请求中的Cookie列表。
方法 | 描述 |
---|---|
HttpSession getSession() | 在服务器中获取会话. 参数如果为 true, 则当不存在会话时新建会话; 参数如果为 false, 则当不存在会话时返回 null |
Cookie[] getCookies() | 返回一个数组, 包含客户端发送该请求的所有的 Cookie 对象. 会自动把Cookie 中的格式解析成键值对. |
对于getCookies
方法,由于Cookie能够任意自定义键值对,如果想要获取一般的键值对,可以使用该方法获取,但是如果想要获取特殊的键值对,如SessionId
,可以通过getSession
直接获取,没必要通过getCookies
方法的途径来获取,因为getSession
方法它自动帮助我们获取了SessionId
。
调用getSession
方法所做的事情:
创建会话:
- 第一步,获取cookie里面的
sessionId
字段,相当于会话标识。 - 第二步,判断是否在服务器上存在。
- 第三步,如果不存在,则创建一个新的HttpSession对象,并生成一个新的
sessionId
。 - 第四步,接下来就会以新生成的
sessionId
作为key,生成的HttpSession对象作为value,以键值对形式储存到类似与哈希表的结构中。 - 第五步,返回响应,将
sessionId
通过set-Cookie
字段返回给浏览器,这样浏览器就得到了sessionId
,浏览器就可以将SessionId保存到Cookie中了。
获取会话:
- 第一步,获取cookie里面的
sessionId
字段,相当于会话标识。 - 第二步,判断是否在服务器上存在。
- 第三步,直接查询HttpServlet对象,找到直接作为响应返回给客户端。
关于HttpSession 这个对象本质上也是一个键值对的形式,并且允许程序员在对象中储存任意的键值对数据,但是key必须是String, value随意。
HttpSession里面的每一个键值对称为属性(Attribute),该类中提供了两个方法可以用来获取该对象中的属性和储存属性(键值对)。
方法 | 描述 |
---|---|
Object getAttribute(String name) | 该方法返回在该 session 会话中具有指定名称的对象,如果没有指定名称的对象,则返回 null. |
void setAttribute(String name, Object value) | 该方法使用指定的名称绑定一个对象到该 session 会话 |
boolean isNew() | 判定当前是否是新创建出的会话 |
Cookie类常用方法 Cookie对象包含了两个属性,一个是name
,另一个是value
,而在请求中是以键值对的形式储存的,服务器收到请求后会将键值对形式的cookie转换为Cookie对象。
方法 | 描述 |
---|---|
String getName() | 该方法返回 cookie 的名称。名称在创建后不能改变。(这个值是 Set-Cooke 字段设置给浏览器的) |
String getValue() | 该方法获取与 cookie 关联的值 |
void setValue(String newValue) | 该方法设置与 cookie 关联的值。 |
在响应中添加Cookie 我们可以通过HttpServletResponse
类中的addCookie
方法来在响应中添加Cookie
,它会作为HTTP响应中set-Cookie
字段来进行表示。
方法 | 描述 |
---|---|
void addCookie(Cookie cookie) | 把指定的 cookie 添加到响应中. |
2.常见案例实现
首先我们先得创建Maven文件,引入依赖,创建必要的路径,这些过程在之前介绍Servlet使用已经详细介绍了,这里就不再进行赘述了,我们的重点是前端与后端交互过程的实现。
2.1登录逻辑的实现
我们经常上网查询一些网站什么的,很多网站都会让你先登录,才能使用其中的一些功能,登录完成之后,一般都会跳到一个主页网站,下面我们就来简单地实现一下这一套逻辑。
第一步,约定前后端接口。 我们需要实现两套交互逻辑,一是登录跳转,二是获取主页。 登录跳转约定: 约定使用POST请求,响应采用302重定向。
获取主页约定: 采用GET请求,响应返回一个页面。
第二步,编写前端交互页面 我们的重点是来学习登录的逻辑,因此登录的界面不需要很好看很复杂,只要能够有两个输入框和一个提交按钮让我们输入账号密码就行。目标页面如下:
前面我们约定了登录的跳转采用post请求,由于场景很简单,我们直接使用form表单构造post请求就可以了。
<!DOCTYPE html> <html lang="ch"> <head> <meta charset="UTF-8"> <title>login</title> </head> <body> <form action="login" method="post"> <input type="text" name="username"> <input type="password" name="password"> <input type="submit" value="登录"> </form> </body> </html>
其中input
标签的name
属性就对应键值对的key
,输入的内容就对应键值对的value
。
第三步,编写后端处理代码
对于登录跳转页面post请求处理思路如下:
- 从请求中获取账号与密码。
- 验证账号与密码。
- 如果验证通过,创建会话,并将
username
数据加入到会话中,当然还可以加入其它的属性,比如主页被访问的次数,创建好会话后,重定向到主页index
。 - 如果验证不通过,告知登录失败即可。
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; @WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf8"); resp.setCharacterEncoding("utf8"); //获取用户账号 String username = req.getParameter("username"); String password = req.getParameter("password"); //验证账户 //验证按照正常流程应该从数据库读数据,但是为了便于演示登录的逻辑,我们直接将账号密码写死 //假设正确的账号与密码是 zhangsan 123 if ("zhangsan".equals(username) && "123".equals(password)) { //登录成功 //创建会话,为后续需登录的页面做准备 HttpSession httpSession = req.getSession(true); httpSession.setAttribute("username", username); //初始情况下设置登录次数 httpSession.setAttribute("count", 0); //跳转到目标页面index resp.sendRedirect("index"); } else { //登录失败 resp.getWriter().write("登录失败!"); } } }
获取主页的get请求处理思路:
- 获取会话。
- 取出会话信息,将主页返回次数加
1
并写回到会话信息中。 - 返回一个简单的页面。
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; @WebServlet("/index") public class IndexServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //返回一个主页 //获取会话,参数必须是false HttpSession httpSession = req.getSession(false); //取出会话信息 String username = (String) httpSession.getAttribute("username"); Integer cnt = (Integer) httpSession.getAttribute("count"); //访问次数加1 cnt++; //写回到会话中 httpSession.setAttribute("count", cnt); //构造页面。我们简单构造一下就好 resp.setContentType("text/html; charset=utf8"); resp.getWriter().write("<h4>欢迎您!" + username + "</h4> <h4>这个主页已经被访问了" + cnt + "次</h4>"); } }
抓包结果:
第一次交互,浏览器从服务器上拿到登录页面。 第二次交互,浏览器给服务器一个登录请求,服务器返回响应,重定向页面。 第三次交互,浏览器收到302重定向响应后,再次向服务器发起请求,访问主页。
效果演示:
2.2上传文件
上传文件时,前端需要使用到form表单,表单中需要使用一种特殊的类型,叫做form-data
类型。
提交文件的时候,浏览器会把文件以form-data
的格式构造到Http请求中,服务器可以通过getPart
方法来取的Part
对象(文件),再通过Part
对象就能够获取到文件信息了。
方法 | 描述 |
---|---|
Part getPart(String name) | 获取请求中给定 name 的文件 |
Collection getParts() | 获取所有的文件 |
Part类常用方法:
方法 | 描述 |
---|---|
String getSubmittedFileName() | 获取提交的文件名 |
String getContentType() | 获取提交的文件类型 |
long getSize() | 获取文件的大小 |
void write(String path) | 把提交的文件数据写入磁盘文件 |
上传文件的请求采用post请求,使用form表单构造post请求,我们来实现将一个文件上传到本机的一个目录下。
前端交互页面:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>file</title> </head> <body> <form action="upload" method="post" enctype="multipart/form-data"> <input type="file" name="myfile"> <input type="submit" value="提交"> </form> </body> </html>
后端处理代码:
基本实现思路:
- 从请求中获取Part对象。
- 调用Part对象的一些方法,获取文件信息,在磁盘写文件等。
- 返回响应。
- 加上@MultipartConfig注解,不然会出错。
import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; import java.io.IOException; @MultipartConfig @WebServlet("/upload") public class UploadServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取Part对象 Part part = req.getPart("myfile"); //输出文件信息 //文件名 System.out.println("文件名:" + part.getSubmittedFileName()); //文件类型 System.out.println("文件类型:" + part.getContentType()); //文件大小 System.out.println("文件大小:" + part.getSize()); //将文件写入磁盘 part.write("D:\\gtee\\Java代码\\github\\Login\\src\\main\\webapp\\上传.png"); //返回响应 resp.setContentType("text/html; charset=utf8"); resp.getWriter().write("上传成功!"); } }
效果演示:
看看我们的目标目录上有没有上传的图片。
发现我们上传的文件已经拷贝到我们所写的磁盘位置上了,我们上传的文件会储存在响应的body
部分,在这个body中,会使用一段随机的字符串作为标志,表示文件的开始与结束。
以上就是基于Cookie与Session的Servlet API会话管理操作的详细内容,更多关于Servlet API会话管理的资料请关注我们其它相关文章!