本文轉自:http://blog.csdn.net/jiangwei0910410003/article/details/23337043
今天繼續來看看JavaWeb的相關知識,這篇文章主要來講一下Session和Cookie的相關知識,首先我們來看一下Cookie的相關知識:
一、Cookie
簡介:
- //顯示上次訪問的時間
- public void test(HttpServletRequest request,HttpServletResponse response) throws Exception{
- //設置編碼
- response.setCharacterEncoding("utf-8");
- response.setContentType("text/html;charset=utf-8");
- PrintWriter out = response.getWriter();
- out.print("你上一次訪問的時間是:");
- //獲得用戶的時間cookie,並且獲取值,如果第一次的話,是沒有Cookie信息的,所以Cookie數組可能爲null,所以我們要做判斷
- Cookie cookies[] = request.getCookies();
- for(int i=0;cookies != null && i<cookies.length;i++){
- if(cookies[i].getName().equals("lastAccessTime")){
- long cookieValue = Long.parseLong(cookies[i].getValue());
- Date date = new Date(cookieValue);
- out.print(date.toLocaleString());
- }
- }
- //創建每次訪問的時候,我們都會回寫一個Cookie給客戶機,並且將Cookie的有效期設置爲30天,路徑設置成整個web應用
- Cookie cookie = new Cookie("lastAccessTime",System.currentTimeMillis()+"");
- cookie.setMaxAge(30*24*3600);
- cookie.setPath("/ServletDemo");
- response.addCookie(cookie);
- }
//顯示上次訪問的時間
public void test(HttpServletRequest request,HttpServletResponse response) throws Exception{
//設置編碼
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print("你上一次訪問的時間是:");
//獲得用戶的時間cookie,並且獲取值,如果第一次的話,是沒有Cookie信息的,所以Cookie數組可能爲null,所以我們要做判斷
Cookie cookies[] = request.getCookies();
for(int i=0;cookies != null && i<cookies.length;i++){
if(cookies[i].getName().equals("lastAccessTime")){
long cookieValue = Long.parseLong(cookies[i].getValue());
Date date = new Date(cookieValue);
out.print(date.toLocaleString());
}
}
//創建每次訪問的時候,我們都會回寫一個Cookie給客戶機,並且將Cookie的有效期設置爲30天,路徑設置成整個web應用
Cookie cookie = new Cookie("lastAccessTime",System.currentTimeMillis()+"");
cookie.setMaxAge(30*24*3600);
cookie.setPath("/ServletDemo");
response.addCookie(cookie);
}
- //刪除Cookie信息
- public void test1(HttpServletRequest request,HttpServletResponse response){
- //下面是刪除cookie
- Cookie cookie = new Cookie("lastAccessTime",System.currentTimeMillis()+"");
- cookie.setMaxAge(0);//將有效期設置成0
- cookie.setPath("/ServletDemo");//設置的路徑必須要和之前的一樣,否則是刪除不了的
- response.addCookie(cookie);
- }
//刪除Cookie信息
public void test1(HttpServletRequest request,HttpServletResponse response){
//下面是刪除cookie
Cookie cookie = new Cookie("lastAccessTime",System.currentTimeMillis()+"");
cookie.setMaxAge(0);//將有效期設置成0
cookie.setPath("/ServletDemo");//設置的路徑必須要和之前的一樣,否則是刪除不了的
response.addCookie(cookie);
}
這樣我們就成功刪除了lastAccessTime這個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必須一致,否則不會刪除
二、Session
Session是服務器端技術,利用這個技術,服務器在運行時可以爲每一個用戶的瀏覽器創建一個其獨享的session對象,由於session爲用戶瀏覽器獨享,所以用戶在訪問服務器的web資源時,可以把各自的數據放在各自的session中,當用戶再去訪問服務器中的其它web資源時,其它web資源再從用戶各自的session中取出數據爲用戶服務。
同時Session也是一個域對象,我們之前介紹了兩個域對象:ServletContext和Request,那麼Session域的作用範圍是:默認情況下是在一個會話期間,當然這個範圍我們是可以設置的,設置之後可以在多個會話之間。那麼Session的生命週期是:
1.Session什麼時候創建:
某server端程序(如Servlet)調用HttpServletRequest.getSession(true)這樣的語句時纔會被創建。
2.Session什麼時候銷燬:
Session在下列情況下被刪除:
A.程序調用HttpSession.invalidate()
B.距離上一次收到客戶端發送的session id時間間隔超過了session的最大有效時間
C.服務器進程被停止
再次注意關閉瀏覽器只會使存儲在客戶端瀏覽器內存中的session cookie失效,不會使服務器端的session對象失效。
下面在來看一下Session技術原理:
下面在來看一下Session的相關api:
getAttribute(String name)/getAttributeNames()/setAttribute(String name)/removeAttribute(String name):這些方法都是和Session域中的值有關的方法,和之前的ServletContext,Request域的是一樣的
getCreationTime():獲取Session的創建時間
getId():獲取session的id
getServletContext():獲取ServletContext對象
invalidate():刪除session的方法,就是將session設置成無效的
setMaxInactiveInterval(int interval):這個方法設置session的最大有效時間
下面來看一下Session的第一個例子:
- HttpSession session = request.getSession();
- System.out.println("SessionObject:"+session);
- System.out.println("SessionId:"+session.getId());
HttpSession session = request.getSession();
System.out.println("SessionObject:"+session);
System.out.println("SessionId:"+session.getId());
打印Session的對象和ID
我們定義兩個Servlet,將上面的代碼拷貝到Servlet中,訪問第一個Servlet1,然後再訪問第二個Servlet2,我們看一下打印結果:
運行結果:
SessionObject:org.apache.catalina.session.StandardSessionFacade@7244001e
SessionId:96F3F15432E0660E0654B1CE240C4C36
SessionObject:org.apache.catalina.session.StandardSessionFacade@7244001e
SessionId:96F3F15432E0660E0654B1CE240C4C36
我們可以看到我訪問了兩個不同的Servlet,但是通過getSession()方法獲取到的Session是同一個,所以getSession()這個方法內部的執行過程是先看有沒有Session,如果有就不創建了,沒有的話就直接創建。
其實request還有一個方法:getSession(boolean mode)
如果mode是true的話,效果是和getSession()方法的效果一樣的
如果mode是false的話,效果是:如果有Session就返回,沒有的話,就不創建session,而返回一個null
上面我們看到了Session的生命週期,session銷燬的時機並不是關閉瀏覽器,而是在用戶在一段時間內(默認是30分鐘)不使用了,就自動銷燬的,比如:一個人打開一個瀏覽器,開啓一個session,這時候他離開了超過30分鐘,等再回來操作的時候,session就被銷燬了,當然這個有效時間是可以設置的,上面講到了兩種方法:
一種是通過web.xml文件中配置(單位是分鐘):
- <session-config>
- <session-out>10</session-out>
- </session-config>
<session-config>
<session-out>10</session-out>
</session-config>
另一種是通過代碼設置(單位是秒):
session.setMaxInactiveInterval(30*60);
那麼我們getSession()方法是怎麼判斷Session是存在的呢?其實Session的實現原理是基於Cookie的技術實現的,每次我們在創建一個session的時候,都會有一個對應的sessionid,然後會將這個sessionid信息寫入到cookie中,但是這個cookie的有效時間是這個瀏覽器進程中,即這個cookie是在服務器的內存中的,不會回寫給客戶機緩存到磁盤中。所以我們會在一個瀏覽器中使用同一個session的,當我們關閉瀏覽器的時候,這個cookie就被銷燬了,那麼sessionid就沒有了,當我們在打開瀏覽器的時候,服務端又會重新創建一個session。所以我們會誤認爲session的生命週期是瀏覽器進程。當用戶關閉了瀏覽器雖然session cookie已經消失,但session對象仍然保存在服務器端
假如現在有一個人買了幾千本書,準備去付款,但是不小心把瀏覽器關了,這時候在打開瀏覽器的時候就會發現之前買的書都不在了,這種情況是很嚴重的,所以我們要改善這種情況,修改的原理很簡單,我們知道那個sessionid是寫在cookie中的,但是這個cookie默認的情況下只存在服務器的內存中的,不會回寫到客戶機瀏覽器的緩存中。如果我們要是修改的話,只需要修改這個cookie的有效期,將這個cookie回寫到客戶機瀏覽器中即可。
- HttpSession session = request.getSession();
- String sessionId = session.getId();
- Cookie cookie = new Cookie("JSESSIONID",sessionId);//把系統的session id的覆蓋掉
- cookie.setPath("/ServletDemo");
- cookie.setMaxAge(30*360);//30分鐘,因爲session的生命週期是30分鐘
- response.addCookie(cookie);
HttpSession session = request.getSession();
String sessionId = session.getId();
Cookie cookie = new Cookie("JSESSIONID",sessionId);//把系統的session id的覆蓋掉
cookie.setPath("/ServletDemo");
cookie.setMaxAge(30*360);//30分鐘,因爲session的生命週期是30分鐘
response.addCookie(cookie);
默認攜帶sessionid信息的cookie字段是:JESSIONID
我們可以覆蓋這個信息的,將有效期設置成30分鐘,因爲默認的session的有效期是30分鐘,所以如果將cookie的有效期大於30分鐘的話,是沒有意義的,因爲當超過30分鐘之後,session就銷燬了,即使攜帶cookie過去,也是找不到對應的session了,但是我們也是可以設置session的有效時間的。所以我們最好將這個cookie的有效期設置要不超過session的有效期,不然是沒有意義的。運行之後,我們可以查看瀏覽器中的Cookie信息,這裏使用IE瀏覽器,點擊F12鍵,選擇緩存-》查看Cookie信息:
上面的就將sessionid的信息以cookie的形式回寫到客戶機緩存中,當我們關閉瀏覽器中,在開啓瀏覽器的話,還是可以訪問到我們之前的數據的,其實這也就實現了多個瀏覽器之間共享一個session的機制。
但是現在還有一個問題,如果用戶手賤他把瀏覽器設置成禁止Cookie了,那麼又悲劇了,我們服務端寫給客戶機的Cookie,但是客戶機是不接受的,所以這樣也是會丟失數據的。那麼這時候我們又該怎麼辦呀?
一種新技術又產生了,那就是URL重寫:
我們首先來看一下,用戶禁止Cookie之後,我們會發現在同一個瀏覽器進程中,訪問不同的Servlet,數據也是沒有的:
我們這裏定義三個Servlet:IndexServlet,BuyServlet,PayServlet
模擬的場景就是:IndexServlet是首頁,裏面寫入連個超鏈接,分別鏈接到購買物品Servlet:BuyServlet,和付款Servlet:PayServlet,代碼如下:
IndexServlet:
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
- req.getSession();
- String buyUrl = "/ServletDemo/BuyServlet";
- String payUrl = "/ServletDemo/PayServlet";
- resp.getWriter().print("<a href='"+buyUrl+"'>Buy</a><br/>");
- resp.getWriter().print("<a href='"+payUrl+"'>Pay</a><br/>");
- }
protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
req.getSession();
String buyUrl = "/ServletDemo/BuyServlet";
String payUrl = "/ServletDemo/PayServlet";
resp.getWriter().print("<a href='"+buyUrl+"'>Buy</a><br/>");
resp.getWriter().print("<a href='"+payUrl+"'>Pay</a><br/>");
}
BuyServlet:
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
- //購買了一件物品,將這個物品存入到session中,然後將這個sessionid回寫到客戶機,有效時間是30分鐘
- HttpSession session = req.getSession();
- session.setAttribute("store", "air-confication");
- Cookie cookie = new Cookie("JSESSIONID",session.getId());//把系統的session id的覆蓋掉
- cookie.setMaxAge(30*360);
- cookie.setPath("/ServletDemo");
- resp.addCookie(cookie);
- }
protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
//購買了一件物品,將這個物品存入到session中,然後將這個sessionid回寫到客戶機,有效時間是30分鐘
HttpSession session = req.getSession();
session.setAttribute("store", "air-confication");
Cookie cookie = new Cookie("JSESSIONID",session.getId());//把系統的session id的覆蓋掉
cookie.setMaxAge(30*360);
cookie.setPath("/ServletDemo");
resp.addCookie(cookie);
}
PayServlet:
- public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
- //我們從session中拿去商品進行顯示
- HttpSession session = request.getSession();
- response.getWriter().print("you buy store is:"+session.getAttribute("store"));
- }
public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//我們從session中拿去商品進行顯示
HttpSession session = request.getSession();
response.getWriter().print("you buy store is:"+session.getAttribute("store"));
}
我們試驗禁止瀏覽器的Cookie:
使用IE瀏覽器:選擇工具-》Internet選項-》隱私-》高級-》禁止Cookie
這裏有一個問題要注意,瀏覽器使用localhost域名,阻止Cookie是沒有效果的,這個問題糾結了好長時間!我們需要直接使用:127.0.0.1來測試:http://127.0.0.1:8080/ServletDemo/IndexServlet
我們點擊Buy,這時候會回寫一個sessionid,但是客戶機禁止Cookie了,所以瀏覽器沒有緩存sessionid,不信的話我們可以查看瀏覽器中緩存的Cookie信息,是沒有我們的Cookie的。
然後我們返回在點擊Pay的時候發現,購買的東東爲null
這個我們在同一個瀏覽器中都發現購買的東西都不見了,這個很蛋疼的,我們之前不回寫sessionid,即使我們禁止Cookie的話也是沒有影響的,因爲那個Cookie是在瀏覽器進程中的,不會回寫,所以禁止Cookie沒有影響。但是我們現在是回寫了sessionid的Cookie,所以這個問題我們得解決,上面說過了使用URL重寫技術,其實這個技術很簡單的,就是將網站中的所以url後面會攜帶一個sessionid,這樣就可以保證當禁止Cookie的時候,我們還是能夠得到sessionid,然後找到相應的session。測試:
這裏只需要修改IndexServlet中的代碼:
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
- resp.setDateHeader("expires", 0);
- req.getSession();
- String buyUrl = resp.encodeURL("/ServletDemo/BuyServlet");
- String payUrl = resp.encodeURL("/ServletDemo/PayServlet");
- resp.getWriter().print("<a href='"+buyUrl+"'>Buy</a><br/>");
- resp.getWriter().print("<a href='"+payUrl+"'>Pay</a><br/>");
- }
protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
resp.setDateHeader("expires", 0);
req.getSession();
String buyUrl = resp.encodeURL("/ServletDemo/BuyServlet");
String payUrl = resp.encodeURL("/ServletDemo/PayServlet");
resp.getWriter().print("<a href='"+buyUrl+"'>Buy</a><br/>");
resp.getWriter().print("<a href='"+payUrl+"'>Pay</a><br/>");
}
一定要執行req.getSession()這個方法,來生成一個session,不然encodeURL中是不會得到sessionid的
這裏就是重寫了,當然我們直接使用encodeURL這個方法,而不是直接自己去將sessionid拼接到url中
訪問IndexServlet:
我們可以看到響應信息是我們回寫客戶機顯示的連接,url後面是會攜帶jsessionid字段的
這樣,即使用戶禁止了Cookie,也是可以實現數據保留的。但是我們也看到了URL重寫的弊端,對於每個url都必須重寫,這個是很麻煩的,而且還有一個問題,針對URL重寫技術,當我們關閉瀏覽器的時候,再去訪問的時候還是失敗,因爲我們關閉瀏覽器,再去打開瀏覽器訪問IndexServlet的時候,會重新創建一個session,這時候在將sessionid重寫到url中,所以不是之前的session了,至此,也知道了,URL重寫是做不到多個瀏覽器進程之間的session數據共享的,那麼這個問題我們需要去該嗎?這個是沒辦法修改的,只能做到這一步了。
下面在來看一下上面內容的一些細節:
當我們在使用URL重寫技術的時候,我們把瀏覽器中的Cookie不禁止,即保留Cookie和URL重寫這兩種技術
我們會發現當用戶攜帶Cookie過來的時候,url鏈接之後是沒有jsessionid字段了,這個得益於encodeURL方法中做了判斷
所以優先級是Cookie中的sessionid高
下面我們在來通過一個實例來總結一下所講的內容
實例:防止表單重複提交
方法一:前端使用JS技術
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <title>My JSP '1.jsp' starting page</title>
- <meta http-equiv="pragma" content="no-cache">
- <meta http-equiv="cache-control" content="no-cache">
- <meta http-equiv="expires" content="0">
- <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
- <meta http-equiv="description" content="This is my page">
- <script type="text/javascript">
- var iscommitted = false;
- function dosubmits(){
- if(!iscommitted){
- iscommitted = true;
- return true;
- }else{
- return false;
- }
- }
- </script>
- <script type="text/javascript">
- function dosubmit(){
- var input = document.getElementById("submit");
- input.disabled = 'disabled';
- return true;
- }
- </script>
- </head>
- <body>
- <form action="/ServletDemo/FormResubmit1" method="post" οnsubmit="return dosubmits()">
- <input type="text" name="username">
- <input id="submit" type="submit" value="提交">
- </form>
- </body>
- </html>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP '1.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<script type="text/javascript">
var iscommitted = false;
function dosubmits(){
if(!iscommitted){
iscommitted = true;
return true;
}else{
return false;
}
}
</script>
<script type="text/javascript">
function dosubmit(){
var input = document.getElementById("submit");
input.disabled = 'disabled';
return true;
}
</script>
</head>
<body>
<form action="/ServletDemo/FormResubmit1" method="post" οnsubmit="return dosubmits()">
<input type="text" name="username">
<input id="submit" type="submit" value="提交">
</form>
</body>
</html>
我們定義了兩個js函數,這兩個js函數都是可以實現防止表單的重複提交
dosubmits()函數是在用戶提交一次之後就通過一個變量來控制不允許提交
dosubmit()函數是在用戶提交一次之後就將提交按鈕設置成不可點擊
但是使用上面的這種方法來防止表單重複提交是有問題的,首先客戶是可以通過查看文件的源代碼,然後修改的代碼的
而且,這種方式解決不了一些問題,比如:當用戶點擊提交之後,他立刻點擊刷新按鈕或者回退按鈕,然後又可以提交了,所以說前端技術是不能徹底防止表單的重複提交,但是我們還是要這麼做的,因爲這樣做至少還是能起到一定的防止效果的,當然我們還需要在服務器端做處理的,實現原理是:
服務器端首先產生一個隨機數,標記一個唯一的表單,然後將這個隨機數寫入到session域中保存,同時我們將這個隨機數使用轉發技術攜帶給一個jsp/html表單中,在使用隱藏標籤將這個隨機數攜帶給提交的servlet中,然後再這個servlet中拿到這個隱藏標籤的參數值,和我們之前寫入到session域中的那個隨機數進行比較,正確的話,說明是第一次提交,我們這時候就將那個隨機數從session域中刪除,當用戶再一次點擊提交表單的時候,再來判斷的時候,因爲session域中沒有了這個隨機數,所以是提交失敗的
下面來看一下實現代碼:
IndexServlet:
- package com.weijia.servletsession;
- import java.io.IOException;
- import java.security.MessageDigest;
- import java.util.Random;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import sun.misc.BASE64Encoder;
- //表單重複提交
- public class IndexServlet extends HttpServlet{
- private static final long serialVersionUID = 1L;
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
- doPost(req,resp);
- }
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
- resp.setDateHeader("expires", 0);
- try{
- test(req,resp);
- }catch(Exception e){
- e.printStackTrace();
- }
- }
- //產生表單
- public void test(HttpServletRequest request,HttpServletResponse response) throws Exception{
- //產生表單號
- String token = TokenProcessor.generateToken();
- request.getSession().setAttribute("token", token);
- request.getRequestDispatcher("/form.jsp").forward(request, response);
- }
- }
- //單例模式,一個人報數比多個人報數重複率底
- class TokenProcessor{
- private static final TokenProcessor instance = new TokenProcessor();
- private TokenProcessor(){
- }
- public static TokenProcessor getInstance(){
- return instance;
- }
- public static String generateToken(){
- String token = System.currentTimeMillis() + new Random().nextInt() + "";
- try{
- MessageDigest md = MessageDigest.getInstance("md5");
- byte[] md5 = md.digest(token.getBytes());
- //base64
- //0011 0010 1100 1101 0010 1001
- //00001100 00101100 00110100 00101001:最小是0,最大的數是63,共64個數
- //base碼錶
- //數據傳輸的作用
- BASE64Encoder encoder = new BASE64Encoder();
- return encoder.encode(md5);
- }catch(Exception e){
- return null;
- }
- }
- }
package com.weijia.servletsession;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Random;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import sun.misc.BASE64Encoder;
//表單重複提交
public class IndexServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
resp.setDateHeader("expires", 0);
try{
test(req,resp);
}catch(Exception e){
e.printStackTrace();
}
}
//產生表單
public void test(HttpServletRequest request,HttpServletResponse response) throws Exception{
//產生表單號
String token = TokenProcessor.generateToken();
request.getSession().setAttribute("token", token);
request.getRequestDispatcher("/form.jsp").forward(request, response);
}
}
//單例模式,一個人報數比多個人報數重複率底
class TokenProcessor{
private static final TokenProcessor instance = new TokenProcessor();
private TokenProcessor(){
}
public static TokenProcessor getInstance(){
return instance;
}
public static String generateToken(){
String token = System.currentTimeMillis() + new Random().nextInt() + "";
try{
MessageDigest md = MessageDigest.getInstance("md5");
byte[] md5 = md.digest(token.getBytes());
//base64
//0011 0010 1100 1101 0010 1001
//00001100 00101100 00110100 00101001:最小是0,最大的數是63,共64個數
//base碼錶
//數據傳輸的作用
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(md5);
}catch(Exception e){
return null;
}
}
}
這裏面我們定義了一個令牌發生器(隨機數產生器),使用的是單例模式
但是有一個問題就是:使用Random類產生的隨機數的長度是不一定的,我們現在想讓每次得到的隨機數的長度是一樣的,此時我們會想到MD5,首先每個數據的MD5是唯一的,還有MD5的長度是一定的128位(16Bytes),但是還有一個問題那個MD5是一個字節數組,當我們將其轉化成String類型的時候,不管使用什麼碼錶,都會出現亂碼的問題,在隨機數中出現了亂碼,那就等於是死了,所以我們需要解決這個問題,這時候我們想到了BASE64編碼,因爲我們知道任何字節數據通過BASE64編碼之後生成的字節數據的大小是在0~63之間的(原理很簡單,就是將字節數組從左到右,每六位是一組,然後再高位補足兩個0,那麼這個字節大小的範圍就是在0~63了),同時BASE64有一套自己的碼錶,裏面只有大小寫字母和數字,這樣就不會產生亂碼了。
下面在來看一下我們轉發的form.jsp中的表單定義:
- <form action="/ServletDemo/FormResubmit" method="post">
- <input type="hidden" name="token" value="${token}">
- 用戶名:<input type="text" name="username"><br/>
- <input type="submit" value="提交">
- </form>
<form action="/ServletDemo/FormResubmit" method="post">
<input type="hidden" name="token" value="${token}">
用戶名:<input type="text" name="username"><br/>
<input type="submit" value="提交">
</form>
這裏我們使用了隱藏標籤hidden,同時也使用了el表達式來取出之前存入到session域中的token字段值,
下面再來看一下處理這個表單的Servlet:FormResubmit
- package com.weijia.servletsession;
- import java.io.IOException;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- //表單重複提交
- public class FormResubmit extends HttpServlet{
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
- doPost(req,resp);
- }
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
- boolean flag = isTokenValid(req);
- if(!flag){
- System.out.println("請不要重複提交");
- return;
- }
- System.out.println("向數據庫中註冊用戶--------");
- req.getSession().removeAttribute("token");
- }
- //驗證表單提交是否有效,返回true,表示表單可以提交
- public boolean isTokenValid(HttpServletRequest request){
- //首先判斷傳遞過來的表單號是否有效
- String clientToken = request.getParameter("token");
- if(clientToken == null){
- return false;
- }
- //然後再判斷服務器端session域中時候又令牌信息了
- String serverToken = (String)request.getSession().getAttribute("token");
- if(serverToken == null){
- return false;
- }
- //在比較表單攜帶過來的隨機數和session域中的令牌信息是否一致
- if(!clientToken.equals(serverToken)){
- return false;
- }
- return true;
- }
- }
package com.weijia.servletsession;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//表單重複提交
public class FormResubmit extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
boolean flag = isTokenValid(req);
if(!flag){
System.out.println("請不要重複提交");
return;
}
System.out.println("向數據庫中註冊用戶--------");
req.getSession().removeAttribute("token");
}
//驗證表單提交是否有效,返回true,表示表單可以提交
public boolean isTokenValid(HttpServletRequest request){
//首先判斷傳遞過來的表單號是否有效
String clientToken = request.getParameter("token");
if(clientToken == null){
return false;
}
//然後再判斷服務器端session域中時候又令牌信息了
String serverToken = (String)request.getSession().getAttribute("token");
if(serverToken == null){
return false;
}
//在比較表單攜帶過來的隨機數和session域中的令牌信息是否一致
if(!clientToken.equals(serverToken)){
return false;
}
return true;
}
}
這樣我們就可以防止表單的重複提交了,其實上面的實現原理就是struts框架中防止表單重複提交的原理
總結:這一篇主要講解了Session和Cookie的相關知識,到此關於JavaWeb的知識,大體上說的差不多了,後面只剩下一個JSP和標籤庫的相關知識了,這兩篇文章會在後面更新。