使用Servlet和JSP开发Java应用程序 ---- 管理会话
1. 何为管理会话
思考
- 为何登录一个系统,过了一段时间需要重新登录?
- 两次添加购物车,服务器怎么知道是同一个用户添加的?
会话(Session)
- 用户打开浏览器,点击多个超链接,访问多个Web程序的多个资源,整个过程称之为一次会话
- 会话是:用户在一定时间内,对一个Web程序进行一次或多次请求,完成和Web程序和网站的一次交互的过程
- 用一个浏览器打开多个Web程序(淘宝 + 腾讯视频),这属于多个会话
- 用多个浏览器打开一个Web网页,这属于多个会话
- 会话管理是跟踪跨Web页面用户活动的过程(用户在本页面已经登录账户后,当用户操作其他服务时不需要再次登录)
- 会话管理就是一种跨页面传输数据的技术
2. 为何需要会话管理技术
- Http:Hypertext Transfer Protocol
- 超文本传输协议
- 一种控制数据传输的协议
- 一种无状态协议
- 将每一个请求作为独立的请求处理,每个请求单独和服务器建立链接
- 这样服务器无法确认两次请求是否是同一个用户,也就是不维护会话状态
- 因此就无法跟踪用户数据和活动
- 所以需要借助一些会话管理技术来跟踪用户会话期间的活动和相关数据
3. 会话管理技术
用于维护会话信息的方法包括
- 表单隐藏字段 - (Hidden Field)
- URL 重写 - (URL Rewriting)
- Cookie - (客户端技术)
- Session API - (服务器端技术)
4. 表单隐藏字段 - (Hidden Field)
- Hidden Field隐藏字段
- 隐藏字段是隐式提交表单数据的一种方式
- 隐藏字段是不可见的
- 隐藏字段通常在表单中使用,input的type设置为hidden即可,实例如下:< input type=“hidden” name=“id” value=“001”>
- 提交隐藏字段需要指定其name属性和value属性
- 如果在提交表单数据时,不需要用户去提供这个数据,或者说不需要或不希望用户看到这个数据的提交,可以考虑使用隐藏字段,如产品编号等,对用户而言并没有知道的意义
隐藏字段的提交内容依旧会在地址栏中显示 提交之后显示的内容
5. URL 重写 - (URL Rewriting)
URL:Uniform Resource Locator - 统一资源定位器
使用 URL 重写实现
- 传递参数到下一个组件(如Servlet或JSP等)
通过URL重写(修改URL)来追加参数,传递数据给下一个组件(Servlet或JSP) 多个参数使用&进行连接 结果
- 让用户输入一个方便用户记忆和方便搜索引擎收录的URL,然后将用户定向到另一个URL
操作网址
添加 jar 包 注册过滤器 /* 表示所有请求
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
将一个复杂的 URL 改变成一个简单的 URL 将 to 地址重写为 from 地址
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite
PUBLIC "-//tuckey.org//DTD UrlRewrite 4.0//EN"
"http://www.tuckey.org/res/dtds/urlrewrite4.0.dtd">
<urlrewrite>
<rule>
<from>/a</from>
<to>/students/studentInfoServlet?sid=003</to>
</rule>
</urlrewrite>
程序运行后,先执行xml文件,注册过滤器,然后找到urlrewrite.xml文件,将一个复杂的url重写为一个简单的url
使用正则表达式来匹配信息
我们可以使用一个rule元素用于定义一个重写规则 默认规则使用正则表达式匹配
当我们在地址栏中输入001 - 004 时会对应的显示对应的信息
<rule>
<from>^/sis/([0-9]{3})$</from>
<to>/students/studentInfoServlet?sid=$1</to>
</rule>
在xml文件中,有一些符号是有特定意义的,包括<,>,&等如果需要表示一个普通的<,>,&,需要使用特殊字符,比如&字符对应的特殊字符是& 在正则表达式中,中划线是有特殊意义的,表示一个连续的范围,如0-9,a-z等,所以可以使用\中划线表示一个普通的中划线
<rule>
<from>^/sis/([0-9]{3})\-([a-zA-Z]{2,4})$</from>
<to>/students/studentInfoServlet?sid=$1&status=$2</to>
</rule>
<rule>
<from>^/sis/([0-9]{3})/([a-zA-Z]{2,4})$</from>
<to>/students/studentInfoServlet?sid=$1&status=$2</to>
</rule>
对于在地址栏中输入的信息,我们可以通过 Servlet 来得到
URL 重写的优点
- **提供安全性:**避免一些参数名、ID等直接暴露到用户面前,若用户在地址栏乱输入,有效的返回一个 404,比直接返回 500 或一大堆服务器错误信息要好的多
- **美化 URL:**有效的将一些长且复杂的 URL 自己组织成精简的 URL
- **更有利于搜索引擎的收入:**通过优化的 URL,更加有利于搜索引擎的收入
6. Cookie - (客户端技术)
创建 Cookie 并存储到客户端
- Cookie 是一种客户端技术,用于在客户端存储信息
- Cookie 类可用于创建 Cookie
- Servlet 是服务器组件,现在在 Servlet 中创建 Cookie
- Cookie 是服务器端创建,发送给客户端,并存储在客户端的信息
- 一个 Cookie 只能存储一项信息
Cookie 类用于创建 Cookie 一个 Cookie 有一个名称和值构成,名称和值只能是字符串,使用 Cookie 的时候是根据名称获取值
Cookie ck1 = new Cookie("lastAccessTime", time);
Cookie ck2 = new Cookie("randomNumber", num);
Cookie 是服务器端创建,伴随着响应被发送到客户端,并且存储在客户端 所以需要将 Cookie 添加到响应对象,才会被发送响应
resp.addCookie(ck1);
resp.addCookie(ck2);
一个 Cookie 在浏览器会话结束时到期,也就是随着关闭浏览器,Cookie 会被删除 可以通过调用 setMaxAge() 设置一个 Cookie 存在多久,单位是秒
ck1.setMaxAge(7*24*60*60);
ck2.setMaxAge(7*24*60*60);
可以通过设置 max age 为0来导致一个 Cookie 被删除
ck3.setMaxAge(0);
获取 Cookie
- Cookie 是服务器端创建,伴随着响应发送到客户端,并存储到客户端
- 后续发送请求给该程序
- Cookie 会随着请求,作为请求报头,返回给服务器
需要通过请求对象获取存储在客户端的 Cookie 通过请求对象的 getCookies() 获取发回的 Cookie,返回的是一个 Cookie 数组
Cookie[] cookies = req.getCookies();
定义两个变量,存储要获取的 Cookie 的值
String ck1 = "";
String ck2 = "";
遍历 Cookie 数组,查找要使用的 Cookie
for(int i = 0; i < cookies.length; i ++ )
{
Cookie ck = cookies[i];
if("lastAccessTime".equals(ck.getName()))
{
ck1 = ck.getValue();
}
else if("randomNumber".equals(ck.getName()))
{
ck2 = ck.getValue();
}
}
jsp页面中用java表达式获取cookie的值
JSP 提供一些隐式对象,可以在 java 代码段和 java 表达式中使用,request 表示请求对象
<%
Cookie[] cookies = request.getCookies();
if(null == cookies) return;
String ck1 = "";
String ck2 = "";
for(int i = 0; i < cookies.length; i ++ ){
Cookie ck = cookies[i];
if("lastAccessTime".equals(ck.getName()))
ck1 = ck.getValue();
else if("randomNumber".equals(ck.getName()))
ck2 = ck.getValue();
}
%>
Cookie 的简单应用
String uname = req.getParameter("username");
String pwd = req.getParameter("password");
String rem = req.getParameter("rem");
Cookie ck1 = new Cookie("username",uname);
Cookie ck2 = new Cookie("password",pwd);
if(null != rem){
ck1.setMaxAge(14*24*60*60);
ck2.setMaxAge(14*24*60*60);
}else{
ck1.setMaxAge(0);
ck2.setMaxAge(0);
}
resp.addCookie(ck1);
resp.addCookie(ck2);
if("fickler".equals(uname) && "1234".equals(pwd)){
resp.sendRedirect("success.jsp");
}else{
resp.sendRedirect("fail.html");
}
获取存储的cookie数据
<br>账号:${cookie.username.value}
<br>密码:${cookie.password.value}
在页面上显示记住的密码
账 号: <input type="text" name="username" value="${cookie.username.value}" class="form-control">
<br><br>
密 码: <input type="password" name="password" value="${cookie.password.value}" class="form-control">
<br><br>
7. Session API - (服务器端技术)
请求StoreSessionDataServlet向会话中存储数据
如果说有一些数据需要在会话期间存储和使用,可以将数据作为属性添加到HttpSession对象中
调用请求对象的 getSession() 方法获取一个 HttpSession 对象,用于存储会话期间需要使用的数据 getSession() 方法:返回当前会话对象,如果还没有会话对象存在,创建一个返回 req.getSession(false) 方法:返回当前会话对象,如果还没有会话对象存在,返回 null
程序会为每个用户创建一个独享的 HttpSession 对象,用于存储该用户在会话期间需要使用的数据
HttpSession session = req.getSession();
如果有一些数据需要在会话期间存储和使用,可以将数据作为属性添加到 HttpSession 对象中 可以调用 setAttribute() 方法将要在会话期间使用的数据作为属性添加到会话对象中
String uid = UserUtil.generateUserId();
int num = UserUtil.getLuckyNumber();
session.setAttribute("userId", uid);
session.setAttribute("luckyNumber", num);
如果需要存储会话范围的数据,可以获取会话对象,添加属性
HttpSession session = req.getSession();
请求GetSessionDataServlet尝试获取会话中存储的数据
getAttribute()方法返回的是Object类型的数据,如果需要转换类型,需要手动转换
String id = (String)session.getAttribute("userId");
int num = (int)session.getAttribute("luckyNumber");
请求get_session_data1.jsp页面,尝试在jsp页面中用"java表达式"获取存储在Session中的值
JSP 提供一些隐式对象 request 表示请求对象 session 表示会话对象
<%
String id = session.getAttribute("userId").toString();
int num = (int)session.getAttribute("luckyNumber");
%>
请求get_session_data2.jsp页面,尝试在jsp页面中用"EL"表达式获取存储在Session中的值
<%--JSP的EL表达式获取会话对象中的属性--%>
<br><br>* 用户ID:${sessionScope.userId}
<br><br>* 幸运数字:${sessionScope.luckyNumber}
<!--也可以不写sessionScope,直接写属性名-->
<br><br>* 用户ID:${userId}
<br><br>* 幸运数字:${luckyNumber}
结束会话
xml 结束会话 < session-timeout >:以分钟为单位指定会话的超时时间,0 或负数表示会话永远不超时
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
</web-app>
Servlet 结束会话 getSession():有会话,返回当前会话对象,没有创建一个返回 getSession(false):有会话,返回当前会话吗,没有返回 null
在 java 代码中调用 setMaxInactiveInterval() 方法修改会话超时时间,单位是秒
HttpSession session = req.getSession(false);
if(null != session){
session.setMaxInactiveInterval(10*60);
}
调用 invalidate():方法使会话立刻失效
HttpSession session = req.getSession(false);
if(null != session){
session.invalidate();
}
|