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跨域
![](http://i.imgur.com/Szu5o1m.png) ```xml <?xml version='1.0' encoding='utf-8'?> <Context path="/ouzicheng" cookies="false"> </Context> ```
禁用所有web應用的Cookie瀏覽器
![](http://i.imgur.com/dFrvztb.png)
注意:該配置只是讓服務器不能自動維護名爲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