整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          Servlet Session 跟蹤

          TTP 是一種"無狀態(tài)"協(xié)議,這意味著每次客戶端檢索網(wǎng)頁時(shí),客戶端打開一個(gè)單獨(dú)的連接到 Web 服務(wù)器,服務(wù)器會自動不保留之前客戶端請求的任何記錄。

          但是仍然有以下三種方式來維持 Web 客戶端和 Web 服務(wù)器之間的 session 會話:

          Cookies

          一個(gè) Web 服務(wù)器可以分配一個(gè)唯一的 session 會話 ID 作為每個(gè) Web 客戶端的 cookie,對于客戶端的后續(xù)請求可以使用接收到的 cookie 來識別。

          這可能不是一個(gè)有效的方法,因?yàn)楹芏酁g覽器不支持 cookie,所以我們建議不要使用這種方式來維持 session 會話。

          隱藏的表單字段

          一個(gè) Web 服務(wù)器可以發(fā)送一個(gè)隱藏的 HTML 表單字段,以及一個(gè)唯一的 session 會話 ID,如下所示:

          <input type="hidden" name="sessionid" value="12345">
          

          該條目意味著,當(dāng)表單被提交時(shí),指定的名稱和值會被自動包含在 GET 或 POST 數(shù)據(jù)中。每次當(dāng) Web 瀏覽器發(fā)送回請求時(shí),session_id 值可以用于保持不同的 Web 瀏覽器的跟蹤。

          這可能是一種保持 session 會話跟蹤的有效方式,但是點(diǎn)擊常規(guī)的超文本鏈接(<A HREF...>)不會導(dǎo)致表單提交,因此隱藏的表單字段也不支持常規(guī)的 session 會話跟蹤。

          URL 重寫

          您可以在每個(gè) URL 末尾追加一些額外的數(shù)據(jù)來標(biāo)識 session 會話,服務(wù)器會把該 session 會話標(biāo)識符與已存儲的有關(guān) session 會話的數(shù)據(jù)相關(guān)聯(lián)。

          例如,http://w3cschool.cn/file.htm;sessionid=12345,session 會話標(biāo)識符被附加為 sessionid=12345,標(biāo)識符可被 Web 服務(wù)器訪問以識別客戶端。

          URL 重寫是一種更好的維持 session 會話的方式,它在瀏覽器不支持 cookie 時(shí)能夠很好地工作,但是它的缺點(diǎn)是會動態(tài)生成每個(gè) URL 來為頁面分配一個(gè) session 會話 ID,即使是在很簡單的靜態(tài) HTML 頁面中也會如此。

          HttpSession 對象

          除了上述的三種方式,Servlet 還提供了 HttpSession 接口,該接口提供了一種跨多個(gè)頁面請求或訪問網(wǎng)站時(shí)識別用戶以及存儲有關(guān)用戶信息的方式。

          Servlet 容器使用這個(gè)接口來創(chuàng)建一個(gè) HTTP 客戶端和 HTTP 服務(wù)器之間的 session 會話。會話持續(xù)一個(gè)指定的時(shí)間段,跨多個(gè)連接或頁面請求。

          您會通過調(diào)用 HttpServletRequest 的公共方法 getSession() 來獲取 HttpSession 對象,如下所示:

          HttpSession session = request.getSession();
          

          你需要在向客戶端發(fā)送任何文檔內(nèi)容之前調(diào)用 request.getSession()。下面總結(jié)了 HttpSession 對象中可用的幾個(gè)重要的方法:

          Session 跟蹤實(shí)例

          本實(shí)例說明了如何使用 HttpSession 對象獲取 session 會話創(chuàng)建時(shí)間和最后訪問時(shí)間。如果不存在 session 會話,我們將通過請求創(chuàng)建一個(gè)新的 session 會話。

          // 導(dǎo)入必需的 java 庫
          import java.io.*;
          import javax.servlet.*;
          import javax.servlet.http.*;
          import java.util.*;
           
          // 擴(kuò)展 HttpServlet 類
          public class SessionTrack extends HttpServlet {
           
           public void doGet(HttpServletRequest request,
           HttpServletResponse response)
           throws ServletException, IOException
           {
           // 如果不存在 session 會話,則創(chuàng)建一個(gè) session 對象
           HttpSession session = request.getSession(true);
           // 獲取 session 創(chuàng)建時(shí)間
           Date createTime = new Date(session.getCreationTime());
           // 獲取該網(wǎng)頁的最后一次訪問時(shí)間
           Date lastAccessTime = 
           new Date(session.getLastAccessedTime());
           String title = "歡迎回到我的網(wǎng)站";
           Integer visitCount = new Integer(0);
           String visitCountKey = new String("visitCount");
           String userIDKey = new String("userID");
           String userID = new String("ABCD");
           // 檢查網(wǎng)頁上是否有新的訪問者
           if (session.isNew()){
           title = "歡迎來到我的網(wǎng)站";
           session.setAttribute(userIDKey, userID);
           } else {
           visitCount = (Integer)session.getAttribute(visitCountKey);
           visitCount = visitCount + 1;
           userID = (String)session.getAttribute(userIDKey);
           }
           session.setAttribute(visitCountKey, visitCount);
           // 設(shè)置響應(yīng)內(nèi)容類型
           response.setContentType("text/html");
           PrintWriter out = response.getWriter();
           String docType =
           "<!doctype html public \"-//w3c//dtd html 4.0 " + "transitional//en\">\n";
           out.println(docType +
           "<html>\n" +
           "<head><title>" + title + "</title></head>\n" +
           "<body bgcolor=\"#f0f0f0\">\n" +
           "<h1 align=\"center\">" + title + "</h1>\n" +
           "<h2 align=\"center\">Session 信息</h2>\n" +
           "<table border=\"1\" align=\"center\">\n" +
           "<tr bgcolor=\"#949494\">\n" +
           " <th>Session 信息</th><th>值</th></tr>\n" +
           "<tr>\n" +
           " <td>id</td>\n" +
           " <td>" + session.getId() + "</td></tr>\n" +
           "<tr>\n" +
           " <td>Creation Time</td>\n" +
           " <td>" + createTime + 
           " </td></tr>\n" +
           "<tr>\n" +
           " <td>Time of Last Access</td>\n" +
           " <td>" + lastAccessTime + 
           " </td></tr>\n" +
           "<tr>\n" +
           " <td>User ID</td>\n" +
           " <td>" + userID + 
           " </td></tr>\n" +
           "<tr>\n" +
           " <td>Number of visits</td>\n" +
           " <td>" + visitCount + "</td></tr>\n" +
           "</table>\n" +
           "</body></html>");
           }
          }
          

          編譯上面的 Servlet SessionTrack,并在 web.xml 文件中創(chuàng)建適當(dāng)?shù)臈l目。在瀏覽器地址欄輸入 http://localhost:8080/SessionTrack,當(dāng)您第一次運(yùn)行時(shí)將顯示如下結(jié)果:

          歡迎來到我的網(wǎng)站

          Session 信息

          Session 信息值id0AE3EC93FF44E3C525B4351B77ABB2D5Creation TimeTue Jun 08 17:26:40 GMT+04:00 2014Time of Last AccessTue Jun 08 17:26:40 GMT+04:00 2014User IDABCDNumber of visits0

          再次嘗試運(yùn)行相同的 Servlet,它將顯示如下結(jié)果:

          歡迎回到我的網(wǎng)站

          Session 信息

          Session 信息值id0AE3EC93FF44E3C525B4351B77ABB2D5Creation TimeTue Jun 08 17:26:40 GMT+04:00 2014Time of Last AccessTue Jun 08 17:26:40 GMT+04:00 2014User IDABCDNumber of visits1

          刪除 Session 會話數(shù)據(jù)

          當(dāng)您完成了一個(gè)用戶的 session 會話數(shù)據(jù),您有以下幾種選擇:

          • 移除一個(gè)特定的屬性:您可以調(diào)用 public void removeAttribute(String name) 方法來刪除與特定的鍵相關(guān)聯(lián)的值。 to delete the value associated with a particular key.
          • 刪除整個(gè) session 會話:您可以調(diào)用 public void invalidate() 方法來丟棄整個(gè) session 會話。
          • 設(shè)置 session 會話過期時(shí)間:您可以調(diào)用 public void setMaxInactiveInterval(int interval) 方法來單獨(dú)設(shè)置 session 會話超時(shí)。
          • 注銷用戶:如果使用的是支持 servlet 2.4 的服務(wù)器,您可以調(diào)用 logout 來注銷 Web 服務(wù)器的客戶端,并把屬于所有用戶的所有 session 會話設(shè)置為無效。
          • web.xml 配置:如果您使用的是 Tomcat,除了上述方法,您還可以在 web.xml 文件中配置 session 會話超時(shí),如下所示:
           <session-config>
           <session-timeout>15</session-timeout>
           </session-config>
          

          上面實(shí)例中的超時(shí)時(shí)間是以分鐘為單位,將覆蓋 Tomcat 中默認(rèn)的 30 分鐘超時(shí)時(shí)間。

          在一個(gè) Servlet 中的 getMaxInactiveInterval() 方法會返回 session 會話的超時(shí)時(shí)間,以秒為單位。所以,如果在 web.xml 中配置 session 會話超時(shí)時(shí)間為 15 分鐘,那么 getMaxInactiveInterval() 會返回 900。

          pringSecurity-10-Session會話管理

          理解Session

          Http協(xié)議是一種無狀態(tài)協(xié)議所以當(dāng)服務(wù)端需要記錄用戶的狀態(tài)時(shí),需要某種機(jī)制用于識別用戶,這個(gè)機(jī)制就是Session。服務(wù)器通過和用戶約定每一個(gè)請求攜帶一個(gè)id信息,用于統(tǒng)一用戶的請求有了管理,并且區(qū)分不同用戶?;趕ession方案,為讓用戶請求都攜帶同一個(gè)id,并且不妨礙用戶體驗(yàn)的情況下,選擇cookie作為載體是一個(gè)不錯(cuò)的選擇,用戶第一次訪問服務(wù)器的時(shí)候,沒有攜帶id,服務(wù)器端會生成sessionid:session鍵值對,并且發(fā)送sessionid給客戶端添加到cookie中。然后該用戶在之后的訪問中,每一次請求都會將sessionid放到cookie中,使得服務(wù)端可以很容易識別用戶。

          但是有時(shí)候用戶為了保護(hù)個(gè)人信息或者安全考慮會禁用cookie,這時(shí)候cookie就無法使用。因此有時(shí)候服務(wù)還支持用url重寫來實(shí)現(xiàn),比如:

          http://www.baidu.com;jessionid=xxx
          

          URL重寫原本是為了兼容禁用cookie的瀏覽器而設(shè)計(jì)的,但也容易被黑客利用。黑客只需訪問一 次系統(tǒng),將系統(tǒng)生成的sessionId提取并拼湊在URL上,然后將該URL發(fā)給一些取得信任的用戶。只要 用戶在session有效期內(nèi)通過此URL進(jìn)行登錄,該sessionId就會綁定到用戶的身份,黑客便可以輕松享 有同樣的會話狀態(tài),完全不需要用戶名和密碼,這就是典型的會話固定攻擊。

          防御會話固定攻擊

          sessionManagement是一個(gè)會話管理的配置器,其中,防御會話固定攻擊的策略有四種:

          • none:用戶登錄后session不發(fā)生變化
          • newSession:用戶登錄以后創(chuàng)建新的session
          • migrateSession:用戶登錄后創(chuàng)建新的session,但是會將舊的session中數(shù)據(jù)復(fù)制到新的session中。
          • changeSessionId:不創(chuàng)建新的會話,而是使用selert容器提供的會話固定保護(hù),每次登錄訪問之后都更換sessionid,但是沒有新建session會話。默認(rèn)啟動此策略
           http.sessionManagement().sessionFixation().changeSessionId();
          

          會話過期

          除了防御會話固定攻擊,還可以通過SpringSecurity配置會話過期策略,比如會話過期跳轉(zhuǎn)到某個(gè)URL。在Springboot應(yīng)用中有兩種會話超時(shí)設(shè)置的方式,當(dāng)會話超時(shí)之后用戶需要重寫登錄才可以訪問應(yīng)用:

          1. server.servlet.session.timeout=1m
          2. spring.session.timeout=1m

          方式1是springboot應(yīng)用自帶的session超時(shí)設(shè)置,方式2是使用Spring Session之后。提供的session超時(shí)配置,方式2優(yōu)先級高

          在Spring Boot中Session超時(shí)最短的時(shí)間是一分鐘,當(dāng)你的設(shè)置小于一分鐘的時(shí)候,默認(rèn)為一分鐘默認(rèn)超時(shí)時(shí)長是30分鐘

          默認(rèn)情況下session失效以后會跳轉(zhuǎn)到認(rèn)證頁面,我們可以自定義session失效后,響應(yīng)結(jié)果,有以下兩種方式。

          invalidSessionUrl

          invalidSessionUrl作用是session失效后跳轉(zhuǎn)的url,配置如下,在安全配置中心的 configure(HttpSecurity http)方法中添加代碼如下:

          1. 在src\main\resources\templates路徑下添加invalidSession.html
          <!--suppress ALL-->
          <!DOCTYPE html>
          <html xmlns:th="http://www.thymeleaf.org" lang="en">
          <head>
              <meta charset="utf-8">
              <meta http-equiv="X-UA-Compatible" content="IE=edge">
              <title>springboot葵花寶典登錄頁面</title>
              <!-- Tell the browser to be responsive to screen width -->
              <meta name="viewport" content="width=device-width, initial-scale=1">
          </head>
          <body>
          <h1>springboot葵花寶典登錄頁面</h1>
          <h2>session會話失效</h2>
          </body>
          </html>
          
          1. 在controller中添加
              @RequestMapping("/invalidSession")
              public String invalidSession() {
                  return "invalidSession"; // classpath: /templates/login.html
              }
          
          1. 在LearnSrpingSecurity的configure(HttpSecurity http)添加配置
          http.sessionManagement().invalidSessionUrl("/invalidSession")
          

          具體配置如圖

          注意要以上路徑需要配置permitAll()權(quán)限,即無需授權(quán)即可訪問

          測試

          啟動項(xiàng)目登錄后,再次登錄,結(jié)果如下

          invalidSessionStrategy

          session失敗后的策略,配置如下:

          1. 創(chuàng)建com.security.learn.sessionStrategy.CustomInvalidSessionStrategy代碼如下
          public class CustomInvalidSessionStrategy implements InvalidSessionStrategy {
              private  static ObjectMapper objectMapper = new ObjectMapper();
              @Override
              public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
                  Cookie cookie = new Cookie("JSESSIONID", null);
                  cookie.setMaxAge(0);
                  String contextPath = request.getContextPath();
                  String c= contextPath.length() > 0 ? contextPath : "/";
                  cookie.setPath(c);
                  response.addCookie(cookie);
                  // 當(dāng)認(rèn)證失敗后,響應(yīng) JSON 數(shù)據(jù)給前端
                  response.setContentType("application/json;charset=UTF-8");
                  response.getWriter().write(objectMapper.writeValueAsString("策略失效"));
              }
          }
          
          1. 將CustomInvalidSessionStrategy注入容器
          @Configuration
          public class Myconfig {
              @Bean
              @ConditionalOnMissingBean(InvalidSessionStrategy.class)
              public CustomInvalidSessionStrategy customInvalidSessionStrategy(){
                  return  new CustomInvalidSessionStrategy();
              }
          }
          
          1. 添加session失效處理

          在LearnSrpingSecurity的configure(HttpSecurity http)添加配置,代碼如下

          測試

          啟動項(xiàng)目登錄后,再次登錄,結(jié)果如下

          如果您覺得本文不錯(cuò),歡迎關(guān)注,點(diǎn)贊,收藏支持,您的關(guān)注是我堅(jiān)持的動力!

          公眾號 springboot葵花寶典 主要分享JAVA技術(shù),主要包含SpringBoot、SpingCloud、Docker、中間件等技術(shù)

          原創(chuàng)不易,轉(zhuǎn)載請注明出處,感謝支持!如果本文對您有用,歡迎轉(zhuǎn)發(fā)分享!

          于Cookie和Session的功能與工作原理,在這里我就不再敘述了,大家想要了解可以看一下我的上一篇博客,講的還是很細(xì)致的。

          但是之前講的Session是運(yùn)行在一臺服務(wù)器上的,所有的訪問都會到達(dá)我們的唯一服務(wù)器上,這樣我們可以根據(jù)客戶端傳來的sessionID,來獲取session,或在對應(yīng)Session不存在的情況下(session 生命周期到了/用戶第一次登錄),創(chuàng)建一個(gè)新的Session;但是,如果我們在集群環(huán)境下,假設(shè)我們有兩臺服務(wù)器A,B,用戶的請求會由Nginx服務(wù)器進(jìn)行轉(zhuǎn)發(fā)(別的方案也是同理),用戶登錄時(shí),Nginx將請求轉(zhuǎn)發(fā)至服務(wù)器A上,A創(chuàng)建了新的session,并將SessionID返回給客戶端,用戶在瀏覽其他頁面時(shí),客戶端驗(yàn)證登錄狀態(tài),Nginx將請求轉(zhuǎn)發(fā)至服務(wù)器B,由于B上并沒有對應(yīng)客戶端發(fā)來sessionId的session,所以會重新創(chuàng)建一個(gè)新的session,并且再將這個(gè)新的sessionID返回給客戶端,這樣,我們可以想象一下,用戶每一次操作都有1/2的概率進(jìn)行再次的登錄,這樣不僅對用戶體驗(yàn)特別差,還會讓服務(wù)器上的session激增,加大服務(wù)器的運(yùn)行壓力。

          為了解決集群環(huán)境下的seesion共享問題,共有4種解決方案:

          1.粘性session

          粘性session是指Ngnix每次都將同一用戶的所有請求轉(zhuǎn)發(fā)至同一臺服務(wù)器上,即將用戶與服務(wù)器綁定。

          2.服務(wù)器session復(fù)制

          即每次session發(fā)生變化時(shí),創(chuàng)建或者修改,就廣播給所有集群中的服務(wù)器,使所有的服務(wù)器上的session相同。

          3.session共享

          緩存session,使用redis, memcached。

          4.session持久化

          將session存儲至數(shù)據(jù)庫中,像操作數(shù)據(jù)一樣才做session。

          其實(shí),最簡單的兩種方案,就是方案一和方案三,都不需要對session進(jìn)行任何操作,只需要將Nginx和Tomcat上的配置文件修改一下即可。由于我們做集群,訪問量一定是比較大的了,對于第一種方案,如果某臺服務(wù)器發(fā)生故障,此服務(wù)器上的所有用戶的session都會丟失,所以今天我們采用第三種解決方案。

          感謝開源項(xiàng)目tomcat-redis-session-manager,感謝項(xiàng)目的發(fā)起者jcoleman。

          此方案最簡單之處就在于我們無需修改項(xiàng)目,只需要修改Tomcat的context.xml配置文件即可,并且,redis服務(wù)器同樣可以做分布式。

          接下來,我們來講解一下如何配置tomcat,首先,講解一下注意事項(xiàng),因?yàn)轫?xiàng)目的原因,并沒能用maven來管理依賴的包,而在我配置的時(shí)候,很正常的發(fā)生了包版本間的沖突,從redis中取出的序列化后的session無法轉(zhuǎn)換為HttpSession,所以,大家做的時(shí)候一定要注意版本間的兼容問題。

          把我使用的版本給大家做個(gè)參考吧,tomcat-redis-session-manager-1.1.jar,jedis-2.1.0.jar,commons-pool-1.6.jar

          這三個(gè)包需放入 tomcat的安裝目錄下的lib文件夾下,IDE自帶的tomcat可以在配置中查看。

          需注意的是,當(dāng)redis的版本‘過高’時(shí),需要依賴commons-pool2.jar,(具體版本不寫)。

          接下來,我們就講一下最核心的配置,配置context.xml文件(文件路徑workspace\.metadata\.me_tcat7\conf,此處僅是myeclipse編輯器的路徑)

          [html] view plain copy

          1. <Valve className="com.radiadesign.catalina.session.RedisSessionHandlerValve" />
          2. <Manager className="com.radiadesign.catalina.session.RedisSessionManager"
          3. host="127.0.0.1"
          4. port="9313"
          5. database="0"
          6. maxInactiveInterval="60"/>

          這個(gè)里的class路徑具體看tomcat-redis-session-manager里的路徑。

          可能你會遇到重啟tomcat配置文件會被還原的問題,那么你還需要修改另一處的context.xml文件,workspace\Servers\MyEclipse Tomcat v7.0-config文件夾中的,修改內(nèi)容同上。

          接下來,給大家看一下配置完成后的結(jié)果:

          圖1為監(jiān)聽到的redis服務(wù)器的狀態(tài),可以看到我們將session序列化后以sessionID為key存入了redis中,圖2可以看到是客戶端header中的cookie信息(若有疑問可以查看我上一篇的博客),圖三是我在redis客戶端get key為sessionID的記錄,可以驗(yàn)證成功,我們配置是成功了的,因?yàn)槭褂玫淖约旱碾娔X,配置有限,并沒有開幾個(gè)tomcat來測試,不過上述還是很有說服力的(線上已經(jīng)測試過了)。

          希望對你有幫助!


          主站蜘蛛池模板: 无码精品人妻一区二区三区AV| 久久无码AV一区二区三区| 无码乱人伦一区二区亚洲一| 精品少妇ay一区二区三区| 国产乱码精品一区二区三区麻豆| 亚洲综合国产一区二区三区| 日本精品视频一区二区三区| 色狠狠色狠狠综合一区| 中文字幕一区二区人妻| 一区二区三区午夜| 国产在线一区观看| 日韩精品国产一区| 国产精品一区不卡| 最新中文字幕一区| 99在线精品一区二区三区| 日韩精品一区二区三区大桥未久 | 精品少妇ay一区二区三区| 国产精品视频第一区二区三区| 一区国严二区亚洲三区| 综合激情区视频一区视频二区| 久久久无码精品人妻一区| 色噜噜狠狠一区二区三区| 日韩免费无码一区二区三区| 免费视频一区二区| 国产午夜精品一区二区三区嫩草| www亚洲精品少妇裸乳一区二区| 天堂不卡一区二区视频在线观看 | 国产vr一区二区在线观看| 国产一区韩国女主播| 国产产一区二区三区久久毛片国语| 无码人妻av一区二区三区蜜臀| 成人在线观看一区| 国产一区二区三区美女| 无码日韩人妻AV一区免费l| 视频一区视频二区日韩专区| 伦理一区二区三区| 日韩国产精品无码一区二区三区| 亚洲国产高清在线精品一区| 在线成人一区二区| 日韩经典精品无码一区| 久久毛片免费看一区二区三区|