客户端为了实现某一功能,发生了多次的请求响应,从客户端开始访问服务器开始,到访问结束,这期间产生的多次请求响应加在一起称之为一个会话。
每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,程序要想办法为每个用户保存这些数据。
如用户登录过后,应该保存一个用户状态,在会话结束前表明用户一直是登录状态。并且不同的用户之间的登录状态应该互不影响。
我们之前已经学过request域和SerlvetContext域,我们分别分析一下。
Request域太小了,在多次请求中,每次请求响应都是新的Request对象,之前存入的request域中的域属性,在请求结束后随着request域的销毁而消失了。
ServletContext域太大了,所有用户都在操作这个域,必然会发生混乱。
此时我们需要会话相关的技术,保存会话产生的数据。
Cookie
cookie的原理是利用Set-cookie响应头和cookie请求头
cookie将会话产生的数据保存在了客户端中,是客户端技术。
Session
session将会话产生的数据保存在了服务器端,是服务器端的技术
Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。如图-9所示:
cookie是会话相关的技术,可以保存会话产生的数据,客户端技术,将数据保存在客户端,基于set-Cookie响应头和Cookie请求头工作的.
Cookie cookie = new Cookie(String name,String value);
--javax.servlet.http.Cookie类用于创建一个Cookie
setValue与getValue方法
--设置和获取cookie的值
getName方法
--cookie需要在创建时就指定好名字,这个名字一旦指定就不能修改了,如果需要修改只能重新创建一个cookie
setMaxAge与getMaxAge方法
--一个cookie被发送到浏览器时,如果没有设置过MaxAge,这个cookie将会被保存在浏览器的内存中,会一直存在到浏览器关闭内存销毁为止,这样的cookie叫做会话级别的Cookie
--我们也可以使用setMaxAge方法设置cookie的存活时间,这样一来,浏览器收到这个Cookie信息时会将cookie信息以文件的形式保存在浏览器的临时文件夹中,保存到指定的时间到来位置,在这个期间即使多次开关浏览器,cookie信息一直都在.
setPath与getPath方法
--设置浏览器在访问哪个地址及其子地址时,带着cookie信息过来.如果没设置过,那么默认的path是当前发送cookie的Servlet所在的路径
setDomain与getDomain方法
--设置浏览器在访问哪个域名时,带着当前的cookie,默认值当前发送cookie的域名,注意,现代的浏览器只要发现cookie设置过maxage,则认为这个cookie是第三方cookie,会拒绝接受
response.addCookie(Cookie c);//向响应中增加一个cookie,可以在一次响应中增加多个cookie
Cookie [] cs = request.getCookies();//返回请求中所有cookike信息组成的数组,如果请求中没有任何cookie信息此方法返回null
所以如果想要删除一个cookie,只要发送一个同名 同path 的cookie,但是maxAge设置为0,覆盖掉要删除的cookie,覆盖过后,新的cookie立即超时,被浏览器删除,感觉起来就好像旧的cookie被删除了一样。
一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。
浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。
如果创建了一个cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。若希望浏览器将该cookie存储在磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间。将最大时效设为0则是命令浏览器删除该cookie。
注意,删除cookie时,path必须一致,否则不会删除(浏览器通过cookie的name+path来标识一个cookie)
如果用户在登录时勾选了记住用户名,则应该将用户名称用户名保存起来,在用户下次登录时,读取保存的用户名,显示出来。
如果用Session保存,我们知道,Session一旦30分钟不使用就会失效,这个时间显然太短了。
而用Cookie保存,可以设置MaxAge指定保存的时常,直到指定时间结束或用户手动清楚cookie之前,此信息一直存在。
所以我们选择Cookie来实现保存用户名的功能。
在用户登录的Servlet中,当用户登录成功后,判断用户是否勾选过记住用户名,如果用户勾选过,则发送cookie保存用户名,注意MaxAge设置为30天。
//查询数据库是否存在指定用户名密码的用户
try {
SAXReader reader = new SAXReader();
Document dom = reader.read(this.getServletContext().getRealPath("WEB-INF/classes/users.xml"));
Element root = dom.getRootElement();
Element ele = (Element) root.selectSingleNode(
"//user[@username='"+username+"' and @password='"+password+"']"
);
if(ele == null){
//没有找到,提示用户名密码不正确
response.getWriter().write("用户名密码不正确!");
return;
}else{
//用户名密码正确,将用户名保存到session中表明用户登录过
request.getSession().setAttribute("user", username);
//检查用户是否勾选过记住用户名
if("true".equals(request.getParameter("remname"))){
//发送cookie保存用户名
Cookie remnamec = new Cookie("remname",username);
remnamec.setPath("/");
remnamec.setMaxAge(3600*24*30);
response.addCookie(remnamec);
}
//回到主页
response.sendRedirect("/index.jsp");
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务。
生命周期:
当程序第一次调用到request.getSession()代码时,服务器明确的直到了需要用到session了,此时创建session.
如果session超过30分钟(可以在web.xml中配置的)没人使用,服务器认为这个session超时了,销毁session.
明确的调用session.invalidate(),session立即销毁.
服务器被非正常关闭或web应用被移除出容器,此时随着web应用的销毁session销毁.如果是正常关闭,session会被钝化.当下次服务器正常启动时,没有超时的session还会被活化回来.
作用范围:
整个会话范围内可见
主要作用:
在会话范围内共享数据
所谓的登录,就是要将登录过后的用户状态保存起来,在后续的请求中能够获知这种状态,这就是典型的在会话中保存数据的过程。
我们可以在Session中保存一个登录标记,表明当前用户已经登录过。后续需要时只需检查Session中的该标记即可判断出用户是否登录过
在Servlet我们获取用户提交的用户名密码
//解决请求乱码
request.setCharacterEncoding("utf-8");
//解决响应乱码
response.setContentType("text/html;charset=utf-8");
//获取用户提交的用户名 密码
String username = request.getParameter("username");
String password = request.getParameter("password");
通过查询数据库检查用户名密码是否正确,如果不正确则进行提示,如果正确则在Session中保存用户的登录状态后回到主页
//查询数据库是否存在指定用户名密码的用户
try {
SAXReader reader = new SAXReader();
Document dom = reader.read(this.getServletContext().getRealPath("WEB-INF/classes/users.xml"));
Element root = dom.getRootElement();
Element ele = (Element) root.selectSingleNode("//user[@username='"+username+"' and @password='"+password+"']");
if(ele == null){
//没有找到,提示用户名密码不正确
response.getWriter().write("用户名密码不正确!");
return;
}else{
//用户名密码正确,将用户名保存到session中表明用户登录过
request.getSession().setAttribute("user", username);
//回到主页
response.sendRedirect("/index.jsp");
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
当网络延迟等情况发生时,用户无法确定表单是否已经提交,从而多次点击提交,造成数据多次提交,称为发生了表单重复提交。
真实案例:12306网站购票
生成一个令牌。
在session域中保存令牌。
在表单中隐藏字段保存令牌。
当表单提交时,在处理的Servlet里检查,如果提交的令牌和session中保存的令牌一致,则执行逻辑,并删除session中的令牌。
如果session域中没有令牌,或和提交的令牌不符合,则认为是表单重复提交,提示
jsp中代码
java中代码
验证码提交后该如何进行校验呢?应该在用户获取验证码时,将验证码信息存入session域,在用户提交表单时,将用户提交的验证码和session中保存的验证码进行比较,如果一致,则验证码正确。
ValiImgServlet,如图:
RegistServlet,如图:
regist.jsp,如图:
其实这种编码方式我们见过,当我们用浏览器提交中文数据时,中文数据也是经过URL编码后提交的。对于非iso8859-1的字符,浏览器和服务器默认使用URL编码进行处理。
浏览器和服务器之间是通过HTTP协议通信的
HTTP协议只支持ISO8859-1字符集
所以其实从理论上来说 HTTP协议只能处理 ISO8859-1中具有的字符 不能处理中文
怎么解决HTTP协议携带中文的问题呢? -- URL编码
通过URL编码 客户端将非iso8859-1的字符转换为 URL编码形式 发送给服务器 服务器收到后再进行URL解码 就可以转换回原来的字符了
Java中提供了进行URL编码和解码的类
URLEncoder:static String encode(String s,String enc);
//将指定的字符串按照指定的编码转换为URL编码的形式
URLDecoder:static String decode(String s,String enc);
//将URL编码后的字符串按照指定编码解码为源字符串
改造代码,在发送cookie时,进行URL编码
//检查用户是否勾选过记住用户名
if("true".equals(request.getParameter("remname"))){
//发送cookie保存用户名
Cookie remnamec =
new Cookie("remname",URLEncoder.encode(username, "utf-8"));
remnamec.setPath("/");
remnamec.setMaxAge(3600*24*30);
response.addCookie(remnamec);
}
jsp中,用户名要经过URL解码