会话管理-cookie,session,禁用cookie的URL重写,token的单态设计模式,异常抛出,UUID,MD5,base64编码
2015-11-18 09:38
661 查看
1、会话
会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。会话过程中要解决的一些问题?
每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,程序要想办法为每个用户保存这些数据。
例如:用户点击超链接通过一个servlet购买了一个商品,程序应该想办法保存用户购买的商品,以便于用户点结帐servlet时,结帐servlet可以得到用户购买的商品为用户结帐。
不能使用request或servletcontext来进行存储
保存会话数据的两种技术:
CookieCookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。
Session
Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务。
2、cookie API
javax.servlet.http.Cookie类用于创建一个Cookie,response接口也中定义了一个addCookie方法,它用于在其响应头中增加一个相应的Set-Cookie头字段。 同样,request接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。Cookie类的方法:public Cookie(String name,String value)
setValue与getValue方法
setMaxAge与getMaxAge方法 :没有设置的话,关闭浏览器cookie就失效了,存于缓存。设置了写入硬盘
setPath与getPath方法 /day06:访问day06的时候带cookie
setDomain与getDomain方法:域方法:https://www.taobao.com/这是主机名 .taobao.com是域名!
getName方法 :cookie名称
示例:
显示用户上次访问时间:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.write("这是网站首页!<br>"); out.write("您上次访问时间是:"); //得到上次访问时间 Cookie[] cookies = request.getCookies(); if (cookies == null) { out.write("初次访问"); } else { for (int i = 0; i < cookies.length; i++) { if(cookies[i].getName().equals("LastAccessTime")){ Long time = Long.parseLong(cookies[i].getValue()); Date date = new Date(time); out.write(date.toLocaleString()); } } } //将访问时间写入cookie中 Cookie cookie = new Cookie("LastAccessTime",System.currentTimeMillis()+""); cookie.setMaxAge(3600); response.addCookie(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:
Cookie cookie = new Cookie("LastAccessTime",System.currentTimeMillis()+""); cookie.setMaxAge(0); response.addCookie(cookie);必须设置 地址一致
容器:两种单列和双列:collection和map。
有检索数据的需求就用双列,没有就用单列。
实例:最近浏览过的商品
cookie3:
//首页 public class CookieDemo3 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); //1.显示网站所有商品 out.write("本网站有如下书籍:<br><br>"); Set<Map.Entry<String, Book>> set = DB.getAll().entrySet(); for(Map.Entry<String, Book> me : set){ Book book = me.getValue(); out.write("<a href='/day07/servlet/CookieDemo4?id="+book.getId()+"' target='_blank'>"+book.getName()+"</a>"); out.write("<br/>"); } //2.显示用户曾经浏览过的商品 out.write("<br/><br/>您曾经浏览过的商品:<br/>" ); Cookie cookies[] = request.getCookies(); for(int i=0;cookies!=null && i<cookies.length;i++){ Cookie cookie = cookies[i]; if(cookie.getName().equals("bookHistory")){ String bookHistory = cookie.getValue(); // 2_3 String ids[] = bookHistory.split("\\_"); //[2,3] for(String id: ids){ Book book = (Book) DB.getAll().get(id); out.write(book.getName() + "<br/>"); } } } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } class DB { private static Map<String,Book> map = new LinkedHashMap(); static{ map.put("1", new Book("1","javaweb开发","老张","一本好书")); map.put("2", new Book("2","spring开发","老黎","一本好书")); map.put("3", new Book("3","hibernate开发","老佟","一本好书")); map.put("4", new Book("4","struts开发","老毕","一本好书")); map.put("5", new Book("5","ajax开发","老张","一本好书")); } public static Map getAll(){ return map; } } class Book{ private String id; private String name; private String author; private String description; public Book() { super(); // TODO Auto-generated constructor stub } public Book(String id, String name, String author, String description) { super(); this.id = id; this.name = name; this.author = author; this.description = description; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAuthor() { return author; 4000 } public void setAuthor(String author) { this.author = author; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }cookie4:
ublic class CookieDemo4 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); //1.根据用户带过来的id号,显示商品的详细信息 String id = request.getParameter("id"); Book book = (Book) DB.getAll().get(id); out.write("您要查看的书的详细信息如下:<br/><br/>"); out.write(book.getId() + "<br/>"); out.write(book.getName() + "<br/>"); out.write(book.getAuthor() + "<br/>"); out.write(book.getDescription() + "<br/>"); //2.给用户回送包含当前商品id的cookie String bookHistory = makeHistory(request,id); // 3_2 Cookie cookie = new Cookie("bookHistory",bookHistory); cookie.setMaxAge(1*30*24*3600); response.addCookie(cookie); } private String makeHistory(HttpServletRequest request, String id) { String bookHistory = null; Cookie cookies[] = request.getCookies(); for(int i=0;cookies!=null&&i<cookies.length;i++){ if(cookies[i].getName().equals("bookHistory")){ bookHistory = cookies[i].getValue(); } } // bookHistory=null 1 bookHistory=1 // bookHistory=3_1_5 1 bookHistory=1_3_5 // bookHistory=3_2_5 1 bookHistory=1_3_2 // bookHistory=3_2 1 bookHistory=1_3_2 // bookHistory=null 1 bookHistory=1 if(bookHistory==null){ return id; } List l = Arrays.asList(bookHistory.split("\\_")); //[3,4] //数组 链接 LinkedList<String> list = new LinkedList(); list.addAll(l); if(list.contains(id)){ // bookHistory=3_1_5 1 bookHistory=1_3_5 list.remove(id); list.addFirst(id); }else{ if(list.size()>=3){ // bookHistory=3_2_5 1 bookHistory=1_3_2 list.removeLast(); list.addFirst(id); }else{ // bookHistory=3_2 1 bookHistory=1_3_2 list.addFirst(id); } } StringBuffer sb = new StringBuffer(); //2_3_4 for(String lid: list){ sb.append(lid + "_"); } return sb.deleteCharAt(sb.length()-1).toString(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
3、session
在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。Session和Cookie的主要区别在于:
Cookie是把用户的数据写给用户的浏览器。存于用户,客户端技术
Session技术把用户的数据写到用户独占的session中。存于服务器,服务器技术
Session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。
购物车实例:
书类如上:index:
response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); request.getSession(); out.write("本网站有如下书:<br/>"); Set<Map.Entry<String,Book>> set = DB.getAll().entrySet(); for(Map.Entry<String,Book> me : set){ Book book = me.getValue(); String url = "/day07/servlet/BuyServlet?id=" + book.getId(); url = response.encodeURL(url); out.println(book.getName() + " <a href='"+url+"'>购买</a><br/>"); }buy:
String id = request.getParameter("id"); Book book = (Book) DB.getAll().get(id); //得到用户想买的书 HttpSession session = request.getSession(); /*Cookie cookie = new Cookie("JSESSIONID",session.getId()); cookie.setMaxAge(30*60); cookie.setPath("/day07"); response.addCookie(cookie);*/ List list = (List) session.getAttribute("list"); //得到用户用于保存所有书的容器 if(list==null){ list = new ArrayList(); session.setAttribute("list", list); } list.add(book); //request.getRequestDispatcher("/servlet/ListCartServlet").forward(request, response); String url = response.encodeRedirectURL("/day07/servlet/ListCartServlet"); System.out.println(url); response.sendRedirect(url);ListCartServlet:
response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); HttpSession session = request.getSession(); List<Book> list = (List) session.getAttribute("list"); if(list==null || list.size()==0){ out.write("对不起,您还没有购买任何商品!!"); return; } //显示用户买过的商品 out.write("您买过如下商品:<br>"); for(Book book : list){ out.write(book.getName() + "<br/>"); }
服务器是如何实现一个session为一个用户浏览器服务的?
浏览器第一次getsession的时候,服务器创建一个session对象,服务器会给每个session一个固定的ID号码,通过cookie的形式保存到用户硬盘上,由于没有设置有效时间,因此,当关闭浏览器这个cookie就失效了,那么这个session就结束了。
因此要实现关闭了浏览器,session对象还在,那么就需要对保存session的ID号码的cookie对象设置有效时间(由于毕竟是session对象,服务器默认该session如果30分钟内没人使用自动销毁,因此自己设置的cookie有效期不应该30分钟)
String id = session.getId(); Cookie cookie = new Cookie("JSESSIONID",id); cookie.setMaxAge(1800);//不应该超过30分钟 cookie.setPath("/"); response.addCookie(cookie);
cookie.setPath("/");应用于所有工程下的servlet!
禁用Cookie后servlet共享数据导致的问题:URL重写
利用URL携带session的ID,网站上的所有URL地址都要重写
response. encodeRedirectURL(java.lang.String url)
用于对sendRedirect方法后的url地址进行重写。
response. encodeURL(java.lang.String url)
用于对表单action和超链接的url地址进行重写
index:
response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); request.getSession(); out.write("本网站有如下图书:<br>"); Set<Map.Entry<String,Book>> set = DB.getAll().entrySet(); for (Map.Entry<String,Book> me:set){ Book book = me.getValue(); String url = response.encodeRedirectURL("/Buy?id=" + book.getID()); out.write(book.getName()+" <a href='"+ url +"'>购买</a><br>"); }buy:
String url = response.encodeRedirectURL("/ListCart"); response.sendRedirect(url);
禁用cookie的话,就不能实现关闭浏览器数据还在
1、服务器是如何做到一个session为一个浏览器的多次请求而服务
1.1 服务器创建session出来后,会把 session的id号,以cookie的形式回写给客户机,这样,只要客户机的浏览器不关,再
去访问服务器时,都会带着session 的id号去,服务器发现客户机带session id过来了,就会使用内存中与之对应的
session为之服务
2、如何做到一个session为多个浏览器服务
2.1 服务器第一次创建session,程序员把session id号,手工以cookie的形式回送给浏览器,并设置cookie的有效期
这样,即使用户的浏览器关了,开新浏览器时,还会带着session id找服务器,服务器从而就可以用内存中与之对应的
session为第二个浏览器窗口服务
3、如何做用户禁用cookie后,session还能为多次请求而服务
3.1 把用户可能点的每一个超链接后面,都跟上用户的session id号
4、session对象的创建和销毁时机
4.1 用户第一次request.getSession时
4.2 session对象默认30分钟没有使用,则服务器会自动销毁session,
4.2.1 用户在web.xml文件中手工配置session的失效时间:session-config timeout
4.2.2 用户可以手工调用session.invalidate方法,摧毁session
购物网站可以用cookie也可以用session,cookie可以减轻服务器压力。
4、session案例
(1)、使用Session完成用户登录
index.jsp<body> 欢迎您:<br> <% User user = (User) session.getAttribute("user"); if (user != null) out.write(user.getUsername()); %> <a href="login.jsp">登录</a> </body>
Login(servlet)
response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); String username = request.getParameter("username"); String password = request.getParameter("password"); User user = DB.find(username, password); if(user == null){ out.write("用户名或密码错误"); return; } request.getSession().setAttribute("user",user); response.sendRedirect("/index.jsp");
login.jsp
<body> <form action="/Login" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> <input type="submit" value="登录"> </form> </body>
(2)、防止表单重复提交
情况:a、延时导致用户多次提交b、提交后多次刷新
c、后退提交后退提交
需要前台与后台的共同努力:
前台:页面利用JavaScript代码进行:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <script type="text/javascript"> function dosubmit(){ var submit = document.getElementById("submit"); submit.disabled="disabled"; return true; } </script> <body> <form action="FormTest" method="post" onsubmit="return dosubmit()"> <input type="text" name="username"><br> <input type="submit" value="提交" id="submit"> </form> </body> </html>
发布表单的同时发布一个随机数,如果随机数一致了,录入信息,并将session中的随机数清除,再提交的时候由于随机数销毁了,就不会一致了防止了表单的重复提交。
随机数的产生应该使用单态设计模式:
单态设计模式:一个类只生成一个实体对象
*1、把类的构造函数私有*2、自己创建一个类的对象
*3、对外提供一个公共的方法,返回类的对象
class TokenProcess{ //单态模式,原子模式,单例模式 /* *单态设计模式(保证类的对象在内存中只有一个) *1、把类的构造函数私有 *2、自己创建一个类的对象 *3、对外提供一个公共的方法,返回类的对象 * */ private TokenProcess(){} private static final TokenProcess instance = new TokenProcess(); public static TokenProcess getInstance(){ return instance; } //形成随机数 public String makeToken(){ String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + ""; //数据指纹 128位长 16个字节 md5 try { MessageDigest md5 = MessageDigest.getInstance("md5"); byte[] digest = md5.digest(token.getBytes()); BASE64Encoder encoder = new BASE64Encoder(); return encoder.encode(digest); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } }
ServletContext:其他人来了共用了
request:请求都变了
session:√
不用UUID:通用唯一识别码 (Universally Unique Identifier)
//base64编码--任意二进制编码明文字符 adfsdfsdfsf
3个字节变成4个字节,3个字节24位,4个字节各取6位,自后向前,取完加两个0,形成一个字节
BASE64Encoder
一般都是向上抛运行异常:throw new RuntimeException(e);但是如果是想用异常当做一种返回值处理,需要人家处理,则应该抛编译异常。
//数据指纹 128位长 16个字节 md5算法 不可逆算法 MessageDigest
try { MessageDigest md5 = MessageDigest.getInstance("md5"); byte[] digest = md5.digest(token.getBytes()); BASE64Encoder encoder = new BASE64Encoder(); return encoder.encode(digest); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); }
(3)、利用Session实现一次性验证码
相关文章推荐
- C#后台线程和UI的交互
- Burp Suite使用介绍
- Burpsuite sqlmap插件
- UITabBarController UITabBarItem 终极一法
- 值动画
- Call requires permission which may be rejected by user: code should explicitly check to see if permi
- getRequestDispatcher()与sendRedirect()的区别
- 关于PowerBuilder启动时出现磁盘符号的解决办法
- UITableView
- Java面试作业,给一个整型数组,要求算出最多相同value的sum,类似二叉树搜索;
- 3.7.2 - Escape Sequences
- SQLiteDatabase中query、insert、update、delete方法参数说明
- 【HDOJ】3337 Guess the number
- android 开发之 Bluetooth
- 移动app交互设计:如何把“手势流”装进手机
- easyUI numberbox框限制输入的数字保留两位小数
- easyui combobox 高度自适应属性设置
- UIViewController之[自定义容器视图控制器]
- 设置UITabbar的字体颜色和被选中的颜色
- IOS 键盘调用 UITExtFieldDelegate