Session 是另外一种记录浏览器状态的机制。不一样的是Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务器把用户的信息以某种的形式记录在服务器,这就是Session
若是说Cookie是检查用户身上的”通行证“来确认用户的身份,那么Session就是经过检查服务器上的”客户明细表“来确认用户的身份的。Session至关于在服务器中创建了一份“客户明细表”。javascript
Session比Cookie使用方便,Session能够解决Cookie解决不了的事情【Session能够存储对象,Cookie只能存储字符串。】。html
从上面的API看出,Session有着request和ServletContext相似的方法。其实Session也是一个域对象。Session做为一种记录浏览器状态的机制,只要Session对象没有被销毁,Servlet之间就能够经过Session对象实现通信java
//获得Session对象 HttpSession httpSession = request.getSession(); //设置Session属性 httpSession.setAttribute("name", "看完博客就要点赞!!");
//获取到从Servlet4的Session存进去的值 HttpSession httpSession = request.getSession(); String value = (String) httpSession.getAttribute("name"); System.out.println(value);
<session-config> <session-timeout>20</session-timeout> </session-config>
<session-config> <session-timeout>20</session-timeout> </session-config>
//设置Session最长超时时间为60秒,这里的单位是秒 httpSession.setMaxInactiveInterval(60); System.out.println(httpSession.getMaxInactiveInterval());
response.setContentType("text/html;charset=UTF-8"); PrintWriter printWriter = response.getWriter(); printWriter.write("网页上全部的书籍:" + "<br/>"); //拿到数据库全部的书 LinkedHashMap<String, Book> linkedHashMap = DB.getAll(); Set<Map.Entry<String, Book>> entry = linkedHashMap.entrySet(); //显示全部的书到网页上 for (Map.Entry<String, Book> stringBookEntry : entry) { Book book = stringBookEntry.getValue(); String url = "/ouzicheng/Servlet6?id=" + book.getId(); printWriter.write(book.getName()); printWriter.write("<a href='" + url + "'>购买</a>"); printWriter.write("<br/>"); }
//获得用户想买书籍的id String id = request.getParameter("id"); //根据书籍的id找到用户想买的书 Book book = (Book) DB.getAll().get(id); //获取到Session对象 HttpSession httpSession = request.getSession(); //因为用户可能想买多本书的,因此咱们用一个容器装着书籍 List list = (List) httpSession.getAttribute("list"); if (list == null) { list = new ArrayList(); //设置Session属性 httpSession.setAttribute("list",list); } //把书籍加入到list集合中 list.add(book);
//获得用户想买书籍的id String id = request.getParameter("id"); //根据书籍的id找到用户想买的书 Book book = (Book) DB.getAll().get(id); //获取到Session对象 HttpSession httpSession = request.getSession(); //建立List集合 List list = new ArrayList(); list.add(book); httpSession.setAttribute("list", list);
//获得用户想买书籍的id String id = request.getParameter("id"); //根据书籍的id找到用户想买的书 Book book = (Book) DB.getAll().get(id); //获取到Session对象 HttpSession httpSession = request.getSession(); //因为用户可能想买多本书的,因此咱们用一个容器装着书籍 List list = (List) httpSession.getAttribute("list"); if (list == null) { list = new ArrayList(); //设置Session属性 httpSession.setAttribute("list",list); } //把书籍加入到list集合中 list.add(book); String url = "/ouzicheng/Servlet7"; response.sendRedirect(url);
//要获得用户购买过哪些书籍,获得Session的属性遍历便可 HttpSession httpSession = request.getSession(); List<Book> list = (List) httpSession.getAttribute("list"); if (list == null || list.size() == 0) { printWriter.write("对不起,你尚未买过任何商品"); } else { printWriter.write("您购买过如下商品:"); printWriter.write("<br/>"); for (Book book : list) { printWriter.write(book.getName()); printWriter.write("<br/>"); } }
//获得Session对象 HttpSession httpSession = request.getSession(); //设置Session属性 httpSession.setAttribute("name", "看完博客就要点赞!!");
String value = (String) request.getSession().getAttribute("name"); printWriter.write(value);
上面说了Session是依靠Cookie来识别用户浏览器的。若是个人用户浏览器禁用了Cookie了呢?绝大多数的手机浏览器都不支持Cookie,那个人Session怎么办?web
HttpServletResponse类提供了两个URL地址重写的方法:数据库
String url = "/ouzicheng/Servlet7"; response.sendRedirect(response.encodeURL(url));
禁用本身项目的Cookie跨域
 ```xml <?xml version='1.0' encoding='utf-8'?> <Context path="/ouzicheng" cookies="false"> </Context> ```
禁用所有web应用的Cookie浏览器

注意:该配置只是让服务器不能自动维护名为jsessionid的Cookie,并不能阻止Cookie的读写。缓存
private String username = null; private String password = null; public User() { } public User(String username, String password) { this.username = username; this.password = password; } ....各类set、get方法
private static List<User> list = new ArrayList<>(); //装载些数据进数据库 static { list.add(new User("aaa","111")); list.add(new User("bbb","222")); list.add(new User("ccc","333")); } //经过用户名和密码查找用户 public static User find(String username, String password) { for (User user : list) { if (user.getUsername().equals(username) && user.getPassword().equals(password)) { return user; } } return null; }
<form action="/ouzicheng/LoginServlet" method="post"> 用户名:<input type="text" name="username"><br/> 密码:<input type="password" name="password"><br/> <input type="submit" value="提交"> </form>
String username = request.getParameter("username"); String password = request.getParameter("password"); User user = UserDB.find(username, password); //若是找不到,就是用户名或密码出错了。 if (user == null) { response.getWriter().write("you can't login"); return; } //标记着该用户已经登录了! HttpSession httpSession = request.getSession(); httpSession.setAttribute("user", user); //跳转到其余页面,告诉用户成功登录了。 response.sendRedirect(response.encodeURL("index.jsp"));
重复提交的危害:tomcat
首先咱们来看一下常见的重复提交。安全
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>表单提交</title> <script type="text/javascript"> //定义一个全局标识量:是否已经提交过表单数据 var isCommitted = false; function doSubmit() { //false表示的是没有提交过,因而就可让表单提交给Servlet if(isCommitted==false) { isCommitted = true; return true; }else { return false; } } </script> </head> <body> <form action="/ouzicheng/Servlet7" onsubmit="return doSubmit()"> 用户名:<input type="text" name="username"> <input type="submit" value="提交"> </form> </body> </html>
<script type="text/javascript"> function doSubmit() { var button = document.getElementById("button"); button.disabled = disabled; return true; } </script>
此时,咱们就想到了,在表单中还有一个隐藏域,能够经过隐藏域把数据交给服务器。
/* * 产生随机数就应该用一个对象来生成,这样能够避免随机数的重复。 * 因此设计成单例 * */ public class TokenProcessor { private TokenProcessor() { } private final static TokenProcessor TOKEN_PROCESSOR = new TokenProcessor(); public static TokenProcessor getInstance() { return TOKEN_PROCESSOR; } public static String makeToken() { //这个随机生成出来的Token的长度是不肯定的 String token = String.valueOf(System.currentTimeMillis() + new Random().nextInt(99999999)); try { //咱们想要随机数的长度一致,就要获取到数据指纹 MessageDigest messageDigest = MessageDigest.getInstance("md5"); byte[] md5 = messageDigest.digest(token.getBytes()); //若是咱们直接 return new String(md5)出去,获得的随机数会乱码。 //由于随机数是任意的01010101010,在转换成字符串的时候,会查gb2312的码表,gb2312码表不必定支持该二进制数据,获得的就是乱码 //因而乎通过base64编码成了明文的数据 BASE64Encoder base64Encoder = new BASE64Encoder(); return base64Encoder.encode(md5); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } }
//生出随机数 TokenProcessor tokenProcessor = TokenProcessor.getInstance(); String token = tokenProcessor.makeToken(); //将随机数存进Session中 request.getSession().setAttribute("token", token); //跳转到显示页面 request.getRequestDispatcher("/login.jsp").forward(request, response);
<form action="/ouzicheng/Servlet7" > 用户名:<input type="text" name="username"> <input type="submit" value="提交" id="button"> <%--使用EL表达式取出session中的Token--%> <input type="hidden" name="token" value="${token}" > </form>
String serverValue = (String) request.getSession().getAttribute("token"); String clientValue = request.getParameter("token"); if (serverValue != null && clientValue != null && serverValue.equals(clientValue)) { System.out.println("处理请求"); //清除Session域对象数据 request.getSession().removeAttribute("token"); }else { System.out.println("请不要重复提交数据!"); }
实现原理是很是简单的:
//在内存中生成图片 BufferedImage bufferedImage = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB); //获取到这张图片 Graphics2D graphics2D = (Graphics2D) bufferedImage.getGraphics(); //设置背景色为白色 graphics2D.setColor(Color.white); graphics2D.fillRect(0, 0, 80, 20); //设置图片的字体和颜色 graphics2D.setFont(new Font(null, Font.BOLD, 20)); graphics2D.setColor(Color.BLUE); //生成随机数 String randomNum = makeNum(); //往这张图片上写数据,横坐标是0,纵坐标是20 graphics2D.drawString(randomNum, 0, 20); //将随机数存进Session域中 request.getSession().setAttribute("randomNum", randomNum); //控制浏览器不缓存该图片 response.setHeader("Expires", "-1"); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); //通知浏览器以图片的方式打开 response.setHeader("Content-type", "image/jpeg"); //把图片写给浏览器 ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
private String makeNum() { Random random = new Random(); //生成0-6位的随机数 int num = random.nextInt(999999); //验证码的数位全都要6位数,因而将该随机数转换成字符串,不够位数就添加 String randomNum = String.valueOf(num); //使用StringBuffer来拼凑字符串 StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < 6 - randomNum.length(); i++) { stringBuffer.append("0"); } return stringBuffer.append(randomNum).toString(); }
<form action="/ouzicheng/Login2Servlet"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> 验证码:<input type="text" name="randomNum"> <img src="/ouzicheng/ImageServlet" ><br><br> <input type="submit" value="提交"> </form>
//获取用户输入验证码的数据 String client_randomNum = request.getParameter("randomNum"); //获取Session中的数据 String session_randomNum = (String) request.getSession().getAttribute("randomNum"); //判断他俩数据是否相等,用户是否有输入验证码,Session中是否为空 if (client_randomNum == null || session_randomNum == null || !client_randomNum.equals(session_randomNum)) { System.out.println("验证码错误了!!!"); return ; } //下面就是验证用户名和密码...................
对于校验码实现思路是这样子的:
从存储方式上比较
从隐私安全上比较
从有效期上比较
从对服务器的负担比较
从浏览器的支持上比较
从跨域名上比较
Cookie cookie = new Cookie("JSESSIONID",session.getId()); cookie.setMaxAge(30*60); cookie.setPath("/ouzicheng/"); response.addCookie(cookie);
若是文章有错的地方欢迎指正,你们互相交流。习惯在微信看技术文章的同窗,能够关注微信公众号:Java3y