1. 四大作用域对象
作用域对象存在的意义: 为了在多个web组件之间传递和共享数据。
public class TestServletScope extends HttpServlet { //作用域经典案例测试 protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1. request 表示当前请求的范围 if(request.getAttribute("count")==null) { request.setAttribute("count", 1); }else { request.setAttribute("count", (Integer)(request.getAttribute("count"))+1); } //2. session 表示当前会话的范围 HttpSession session = request.getSession(); if(session.getAttribute("count")==null) { session.setAttribute("count", 1); }else { session.setAttribute("count", (Integer)(session.getAttribute("count"))+1); } //3. application 表示当前应用的范围 ServletContext application = request.getServletContext(); if(application.getAttribute("count")==null) { application.setAttribute("count", 1); }else { application.setAttribute("count", (Integer)(application.getAttribute("count"))+1); } //请求重定向 request.getRequestDispatcher("result").forward(request, response); } }
public class ResultServlet extends HttpServlet { protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //request int count1 = (Integer)request.getAttribute("count"); response.getWriter().println("request:"+count1); //session HttpSession session = request.getSession(); int count2 = (Integer)session.getAttribute("count"); response.getWriter().println("session:"+count2); //application ServletContext app = request.getServletContext(); int count3 = (Integer)app.getAttribute("count"); response.getWriter().println("application:"+count3); } }
运行结果:request值一直不变。当重新请求 session值加一,,关闭浏览器后 session值变为1 . 只有关闭tomcat ,application 才变成1,不然重新请求一直递增。
2. 请求转发forward和URL重定向的区别?
请求转发:request.getRequestDispatcher("login.jsp").forward(request, response);
浏览器的请求发送给组件1,组件1经过一些处理之后,将request和response对象“传递”给组件2,由组件2继续处理,然后输出响应(当然,也可以继续向其他组件“传递”),这个传递的过程称之为“转发”。
整个过程只涉及一次浏览器和服务器之间的“请求-响应”,转发过程中的组件共享同一个请求(request)和响应(response)对象。
特点:
1. 转发过程中浏览器地址栏路径没变,共享同一个请求对象,在请求中共享数据,响应由最后一个决定。
2. 只能够访问当前应用中的资源,包括WEB-INF中的资源,不能够跨域跳转(老师我想跳转到源代码官网去看视频...)
3.一般用于用户登陆的时候,根据角色转发到相应的模块.
4. 疑问: 既然可以访问WEB-INF中的资源了,那怎么之前又说放在里安全呢?
a) 程序没有提供的路径就不能够访问;
b) 在跳转之前可以做权限判断
重定向:response.sendRedirect("login.jsp");
是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
特点:
1. 浏览器中地址会变,发送了两个请求,相当于访问了两次。
2. 因为是不同的请求,所以不能够共享请求中的数据,最终的响应是由最后一个Servlet决定。
3. 可以跨域访问资源(尝试访问itsource.cn),不能够访问WEB-INF中的资源
4.一般用于用户注销时返回主页面或跳转到其它的网站等
3.无状态的HTTP
Http:表示超文本的传输协议 。特点:无状态的---多个请求之间,不会去共享信息。
什么是会话?
可简单理解为:用户开一个浏览器,访问某一个web站点,在这个站点点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一次会话。
会话跟踪
HTTP是无状态协议,没有记忆力,不知道哪一个客户端请求了自己,每个请求之间无法共享数据。这就无法知道会话什么时候开始,什么时候结束,也无法确定发出请求的用户身份。
在一次会话中多次请求共享数据即会话跟踪技术.
解决HTTP无状态的问题:参数传递
把我们需要保持状态的数据在每次跳转的时候都传到另一个页面上去。
这种方式有天生的缺陷:
1. 跳转的网页过多,传值麻烦,因为地址栏参数是有限制的且可见,所以传值数据有限,安全性低。
2. 不可以传递对象
小插曲: 保存会话状态的 方式。
1. url重写 ,将会话信息作为请求的 参数传递。
2, 设置表单隐藏域。 将和会话跟踪的字段添加到隐式表单域中,这些信息不会在浏览器中显示,但是提交表单的时候会提交给服务器。
这两种方式很难处理跨越多个页面的信息传递,因为每次修改URL 或者 添加隐式表单域 来存储会话信息,显得很繁琐。
怎么办? 使用Cookie或者Session。
4. 谈谈cookie
4.1 cookie的原理和生命周期
原理: Cookie是由服务器端生成,发送浏览器,浏览器会将Cookie的key/value保存到某个目录下的文本文件内,当浏览器再请求服务器时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器通过检查该Cookie来获取用户状态。(前提是浏览器设置为启用cookie)。
cookie 的生命周期:
生:Cookie在创建之后放到浏览器中
死:关闭浏览器
我们可以自己去设置Cookie的存在时间 cookie.setMaxAge(int second); 正数:代表多少秒 0:就代表删除 负数:关闭浏览器
4.2 添加cookie
获取客户端的Cookie时,只能获取name与value属性,其它属性都不会被提交。
Cookie c = new Cookie("username","peter");// 新建一个Cookie对象 c.setMaxAge(24*60*60); // 设置过期时间1天,以秒为单位 response.addCookie(c); // 保存cookie到客户端
4.3 删除cookie
删除某个Cookie时,只需要新建一个只有maxAge和value不一样的同名Cookie,然后添加到response中覆盖原来的Cookie
Cookie cookie = new Cookie("username","peter");// 新建Cookie cookie.setMaxAge(0); // 设置生命周期为0,表示将要删除 response.addCookie(cookie); // 执行添加后就从response里删除了
4.4 修改cookie
1. 修改某个Cookie时,只需要新建一个只有value属性不一样的同名Cookie,将它扔给浏览器就可以把原来的cookie覆盖了。
Cookie cookie = new Cookie("username","joker");// 新建Cookie cookie.setMaxAge(24*60*60); // 设置生命周期 response.addCookie(cookie); // 执行添加后就从response里覆盖修改了
2. 通过setValue()方法,修改cookie的值。
cookie.setValue("xxxxxxx");//修改cookie 的值 response.addCookie(cookie);//发送给浏览器
注意:修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie而不会覆盖之前的Cookie,从而导致修改、删除失败。
4.5 获取用户请求中的cookie 简单完整示例
@WebServlet("/RegisterServlet") public class register extends HttpServlet { private static final long serialVersionUID = 1L; protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); String username = request.getParameter("name"); //拿到传过来的用户名 Cookie cookie = new Cookie("uname",username); //创建一个cookie response.addCookie(cookie);//还回浏览器 //显示到页面上 response.getWriter().println("欢迎您"+username); response.getWriter().println("<hr/>"); response.getWriter().println("<a href='/TestCookie/ListServlet'>亲爱的,您有多少封邮件未读</a>"); } }
@WebServlet("/ListServlet") public class ListServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); Cookie[] cookies = request.getCookies(); //获取的是请求里的所有cookie组成的数组 String uname = null; //先判断有没有cookie if(cookies!=null) { //遍历所有的cookie for (Cookie cookie : cookies) { String cookieName =cookie.getName();//得到cookie的名称 if("uname".equals(cookieName)) { uname = cookie.getValue(); //得到cookie的值 break; } } } response.getWriter().println("邮件已经没有了"+uname); } }
4.6 setDomain决定允许访问Cookie的域名,而setPath决定允许访问Cookie的路径(ContextPath)
/cookie/login 在这个路径下面保存的Cookie /cookie/single:可以拿到路径的 /cookie2/list :这个就拿不到路径
Cookie默认只存在当前路径的位置。 setPath()设置同一服务器内的cookie使用范围 ,故 c.setPath(“/”),使同一服务器内所有应用都可访问到该Cookie。
Cookie c = new Cookie("name","peter"); c.setMaxAge(24*60*60); c.setPath("/");//同一服务器内所有应用都可访问到该Cookie response.addCookie(c); //保存cookie到客户端
1.如果同一服务器内有两个应用agx1.0和agx2.0 当我们在agx1.0里有c.setPath(“/agx2.0/”);时,该cookie就只能在agx2.0下面能获取到,就连创建该cookie的agx1.0也获取不到。 2.如果在agx1.0里有c.setPath(“/agx2.0/abc/”);时,则只能在agx2.0/abc下面才能获得该cookie,即使在agx2.0下面也不能获取到。
小插曲:
一级域名:又叫顶级域名,一串字符串中间一个点隔开,例如baidu.com,这里说明一下,www.baidu.com不是一级域名!!而是二级域名!
二级域名:实际上就是一个一级域名以下的主机名,一串字符串中间两个“.”隔开,例如pan.baidu.com("pan"就是主机名)。
三级域名:二级域名的子域名,特征是包含三个“.”,一般来说三级域名都是免费的。
为什么直接在地址栏输入一个IP地址也可以跳转到页面呢?
因为在网络上机器彼此连接只能互相识别IP,而数字标识较难记忆,所以才演化出域名来代替IP地址,当我们将在地址栏输入域名欲跳转到某个页面时,点击提交后会由专门的域名解析服务器(DNS服务器)对我们的域名进行解析,得出域名对应的IP地址再进行连接。所以如果我们直接在地址栏输入与域名对应的IP也可以跳转到同一个页面。
cookie的跨域
同一个一级域名下的两个二级域名如www.agx.com和images.agx.com也不能交互使用Cookie,因为二者的域名并不严格相同。
如果想所有agx.com名下的二级域名都可以使用该Cookie,则需要设置Cookie的domain参数为”.agx.com”,例如:
Cookie cookie = new Cookie("name","peter"); // 新建Cookie cookie.setDomain(".agx.com"); // 设置域名 cookie.setPath("/"); // 设置路径 cookie.setMaxAge(Integer.MAX_VALUE); // 设置有效期为永久 response.addCookie(cookie); // 输出到客户端
注意:
domain参数必须以点(“.”)开始。另外,name相同但domain不同的两个Cookie是两个不同的Cookie。
如果想要两个域名完全不同的网站共有Cookie,可以生成两个name相同的Cookie,domain属性分别为两个域名。
4.7 解决cookie中文乱码问题
由于在 Tomcat7中,是不支持中文的。
对于要存储到cookie的数据先进行URLEncode编码,然后在解码。
String username = request.getParameter("name"); Cookie cookie = new Cookie("uname", URLEncoder.encode(username,"utf-8")); response.addCookie(cookie);//还回浏览器
Cookie[] cookies = request.getCookies(); //拿到传过来的用户名,得到cookie。 String uname = null; //先判断有没有cookie if(cookies!=null) { //遍历所有的cookie for (Cookie cookie : cookies) { String cookieName =cookie.getName();//得到cookie的名称 if("uname".equals(cookieName)) { uname = cookie.getValue(); //得到cookie的值 uname = URLDecoder.decode(uname, "utf-8"); } } }
4.8 使用cookie记住密码
方案1:
直接把用户名与密码都保持到Cookie中,下次访问时检查Cookie中的用户名与密码,与数据库比较。这是一种比较危险的选择,一般不把密码等重要信息保存到Cookie中。
方案2:
把密码加密后保存到Cookie中,下次访问时解密并与数据库比较。这种方案略微安全一些。如果不希望保存密码,还可以把登录的时间戳保存到Cookie与数据库中,到时只验证用户名与登录时间戳就可以了。
方案3:
实现方式是把账号按照一定的规则(密钥)加密后,连同账号一块保存到Cookie中。下次访问时只需要判断账号的加密规则是否相同即可。
加 密:
String userName = request.getParameter("userName");//获取用户名 Md5Hash psw = new Md5Hash(userName, "peter.com", 2);//以peter.com为密钥加密 //将用户名添加进cookie并发送给客户端 Cookie userCookie = new Cookie("userName",userName); userCookie.setMaxAge(7*24*60*60); response.addCookie(userCookie); //将密钥生成的密文加进cookie并发送给客户端 Cookie c = new Cookie("key",psw.toString()); c.setMaxAge(7*24*60*60); response.addCookie(c);
解密:
String usreName = null; String key = null; Cookie[] cookie = request.getCookies(); if(cookie !=null){ for(Cookie c:cookie){ // 遍历Cookie if("userName".equals(c.getName()) userName = c.getValue(); if("key".equals(c.getName()) key= cookie.getValue(); } } if(userName != null && key != null){ String secrect = new Md5Hash(userName, "peter.com", 2); if(key.equals(secrect )){//加密规则正确,说明已经登录 //... //....省略后续处理 } }
由上面可知,如果将密码加密后存进cookie后,当我们下次访问登录页时,密码框里的密码将不是用户真正的密码。
5.谈谈session
5.1 session是什么
session 即 会话控制。 session是一个存在服务器上的类似于一个散列表格的文件。里面存有我们需要的信息,在我们需要用的时候可以从里面取出来。类似于一个大号的map吧,里面的键存储的是用户的sessionid,用户向服务器发送请求的时候会带上这个sessionid。这时就可以从中取出对应的值了。
5.2 session 的工作原理
- 客户首次访问服务器的一个页面时,服务器就会为该用户分配一个session对象,同时为这个session指定唯一的ID,并且将该ID发送到客户端并写入到cookie中,使得客户端与服务器的session建立一一对应的关系;
- 当客户端继续访问服务器端的其它资源时,服务器不再为该客户分配新的session对象,直到客户端浏览器关闭、超时或调用session的invalidate()方法使其失效,客户端与服务器的会话结束。
- 当客户重新打开浏览器访问网站时,服务器会重新为客户分配一个session对象,并重新分配sessionID。
注意: 1. Session 由服务器端创建,内容也保存在服务器端,浏览器第一次访问服务器会在服务器端生成一个session,有一个sessionid和它对应。sessionid是一个会话的key,以Cookie的形式存储在客户端,tomcat生成的sessionid叫做jsessionid。
2. 浏览器的cookie被禁止后session就须要用get方法的URL重写的机制或使用POST方法提交隐藏表单的形式来实现。
3. session在访问tomcat服务器HttpServletRequest的getSession(true)的时候创建,tomcat的ManagerBase类提供创建sessionid的方法:随机数+时间+jvmid。
存储在服务器的内存中,tomcat的StandardManager类将session存储在内存中,也可以持久化到file,数据库,memcache,redis等。session销毁只能通过invalidate或超时,关掉浏览器并不会关闭session。
设置session生命周期的三种方法;
方式一:
在Servlet中设置
HttpSession session = request.getSession();
session.setMaxInactiveInterval(60);//单位为秒
方式二:
在web.xml中设置session-config如下:
<session-config>
<session-timeout>2(单位:分钟)</session-timeout>
</session-config>
方式三:
在Tomcat的/conf/web.xml中session-config, 默认值为:30分钟 配置全局Session生命周期,即所有在该服务器中部署的项目都会使用
<session-config>
<session-timeout>30</session-timeout>
</session-config>
优先级:Servlet中API设置 > 程序/web.xml设置 > Tomcat/conf/web.xml设置
使session失效的方法: session.invalidate() ;
session对象主要用于属性操作和会话管理,常用方法如下:
1、public void setAttribute(String name,String value)设定指定名字的属性的值,并将它添加到session会话范围内,如果这个属性是会话范围内存在,则更改该属性的值。 2、public Object getAttribute(String name)在会话范围内获取指定名字的属性的值,返回值类型为object,如果该属性不存在,则返回null。 3、public void removeAttribute(String name),删除指定名字的session属性,若该属性不存在,则出现异常。 4、public void invalidate(),使session失效。可以立即使当前会话失效,原来会话中存储的所有对象都不能再被访问。 5、public String getId( ),获取当前的会话ID。每个会话在服务器端都存在一个唯一的标示sessionID,session对象发送到浏览器的唯一数据就是sessionID,它一般存储在cookie中。 6、public void setMaxInactiveInterval(int interval) 设置会话的最大持续时间,单位是秒,负数表明会话永不失效。 7、public int getMaxInActiveInterval(),获取会话的最大持续时间。 8、使用session对象的getCreationTime()和getLastAccessedTime()方法可以获取会话创建的时间和最后访问的时间,但其返回值是毫秒,一般需要使用下面的转换来获取具体日期和时间。 Date creationTime = new Date(session.getCreationTime()); Date accessedTime = new Date(session.getLastAccessedTime());
Session操作(增删改查)
创建session/得到session/ HttpSession session = req.getSession() 存值 session.setAttribute(String key,Object obj) 取值 session.getAttribute(String key) 删除值 session.removeAttribute(String key)
6.cookie 和session 的区别:
1、保存状态:cookie保存在浏览器端,session保存在服务器端。
2、安全性:针对cookie所存在的攻击:别人可以分析存放在本地的COOKIE并进行Cookie欺骗,Cookie截获;session的安全性大于cookie。
原因如下: (1)sessionID存储在cookie中,若要攻破session首先要攻破cookie; (2)sessionID是要有人登录,或者启动session_start才会有,所以攻破cookie也不一定能得到sessionID; (3)第二次启动session_start后,前一次的sessionID就是失效了,session过期后,sessionID也随之失效。 (4)sessionID是加密的 (5)综上所述,攻击者必须在短时间内攻破加密的sessionID,这很难。
3,性能: session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
考虑到减轻服务器性能方面,应当使用COOKIE。
4、存储的大小:单个cookie保存的数据不能超过4kb,很多浏览器都限制一个站点最多保存20个cookie;session大小没有限制。
5. 存储内容:cookie只能保存字符串类型,以文本的方式保存在客户端;session通过类似与Hashtable的数据结构来保存,能支持任何类型的对象(session中可含有多个对象)
6、所以个人建议:
将登陆信息,网上商城中的购物车 等重要信息存放为SESSION
其他信息如果需要保留,可以放在COOKIE中
7. HTML5 本地存储 WebStorage
存储位置: 保存在客户端,不与服务器进行交互通信,克服由cookie所带来的一些限制。
特性
1、设置、读取方便。
2、容量较大,sessionStorage约5M、localStorage约20M
3、只能存储字符串,可以将对象JSON.stringify() 编码后存储
4. 安全性:WebStorage不会随着HTTP header发送到服务器端,所以安全性相对于cookie来说比较高一些,不会担心截获,但是仍然存在伪造问题;
5. 节省网络流量,存储在本地的数据可以直接获取,减少了客户端和服务器端的交互,节省了网络流量;
两种API:
window.sessionStorage(会话存储)。
在同一个浏览器窗口下数据可以共享,当窗口关闭时数据销毁 ,数据存在内存,生命周期为关闭浏览器窗口。
window.localStorage(本地存储)
可以多窗口共享数据,数据存在硬盘上,永久生效,关闭浏览器不会消失,除非手动删除。
WebStorage提供了一些方法,数据操作比cookie方便:
setItem(key, value) 以键值对的方式储存信息
getItem(key) 通过传入的键值,获取存储内容
removeItem(key) 删除键值为key的存储内容
clear() 清空所有存储内容
key(n) 以索引值来获取存储内容
window.sessionStorage 实例:
window.localStorage 实例: