本篇文章為大家展示了使用SpringSession怎么實(shí)現(xiàn)請(qǐng)求與響應(yīng)重寫,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。
成都創(chuàng)新互聯(lián)主營(yíng)貢山網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,成都APP應(yīng)用開發(fā),貢山h5微信小程序定制開發(fā)搭建,貢山網(wǎng)站營(yíng)銷推廣歡迎貢山等地區(qū)企業(yè)咨詢
1、請(qǐng)求重寫
SpringSession
中對(duì)于請(qǐng)求重寫,在能力上主要體現(xiàn)在存儲(chǔ)方面,也就是 getSession
方法上。在 SessionRepositoryFilter
這個(gè)類中,是通過(guò)內(nèi)部類的方式實(shí)現(xiàn)了對(duì) HttpServletRequset
和 HttpServletResponse
的擴(kuò)展。
1.1 HttpServletRequset 擴(kuò)展實(shí)現(xiàn)
private final class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper { // HttpServletResponse 實(shí)例 private final HttpServletResponse response; // ServletContext 實(shí)例 private final ServletContext servletContext; // requestedSession session對(duì)象 private S requestedSession; // 是否緩存 session private boolean requestedSessionCached; // sessionId private String requestedSessionId; // sessionId 是否有效 private Boolean requestedSessionIdValid; // sessionId 是否失效 private boolean requestedSessionInvalidated; // 省略方法 }
1.2 構(gòu)造方法
private SessionRepositoryRequestWrapper(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) { super(request); this.response = response; this.servletContext = servletContext; }
構(gòu)造方法里面將 HttpServletRequest
、 HttpServletResponse
以及 ServletContext
實(shí)例傳遞進(jìn)來(lái),以便于后續(xù)擴(kuò)展使用。
1.3 getSession 方法
@Override public HttpSessionWrapper getSession(boolean create) { // 從當(dāng)前請(qǐng)求線程中獲取 session HttpSessionWrapper currentSession = getCurrentSession(); // 如果有直接返回 if (currentSession != null) { return currentSession; } // 從請(qǐng)求中獲取 session,這里面會(huì)涉及到從緩存中拿session的過(guò)程 S requestedSession = getRequestedSession(); if (requestedSession != null) { // 無(wú)效的會(huì)話id(不支持的會(huì)話存儲(chǔ)庫(kù))請(qǐng)求屬性名稱。 // 這里看下當(dāng)前的sessionId是否有效 if (getAttribute(INVALID_SESSION_ID_ATTR) == null) { // 設(shè)置當(dāng)前session的最后訪問(wèn)時(shí)間,用于延遲session的有效期 requestedSession.setLastAccessedTime(Instant.now()); // 將requestedSessionIdValid置為true this.requestedSessionIdValid = true; // 包裝session currentSession = new HttpSessionWrapper(requestedSession, getServletContext()); // 不是新的session,如果是新的session則需要改變sessionId currentSession.setNew(false); // 將session設(shè)置到當(dāng)前請(qǐng)求上下文 setCurrentSession(currentSession); // 返回session return currentSession; } } else { // 這里處理的是無(wú)效的sessionId的情況,但是當(dāng)前請(qǐng)求線程 session有效 if (SESSION_LOGGER.isDebugEnabled()) { SESSION_LOGGER.debug( "No session found by id: Caching result for getSession(false) for this HttpServletRequest."); } // 將invalidSessionId置為true setAttribute(INVALID_SESSION_ID_ATTR, "true"); } // 是否需要?jiǎng)?chuàng)建新的session if (!create) { return null; } if (SESSION_LOGGER.isDebugEnabled()) { SESSION_LOGGER.debug( "A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for " + SESSION_LOGGER_NAME, new RuntimeException( "For debugging purposes only (not an error)")); } // 創(chuàng)建新的session S session = SessionRepositoryFilter.this.sessionRepository.createSession(); // 設(shè)置最后訪問(wèn)時(shí)間,也就是指定了當(dāng)前session的有效期限 session.setLastAccessedTime(Instant.now()); // 包裝下當(dāng)前session currentSession = new HttpSessionWrapper(session, getServletContext()); //設(shè)置到當(dāng)前請(qǐng)求線程 setCurrentSession(currentSession); return currentSession; }
上面這段代碼有幾個(gè)點(diǎn),這里單獨(dú)來(lái)解釋下。
getCurrentSession
這是為了在同一個(gè)請(qǐng)求過(guò)程中不需要重復(fù)的去從存儲(chǔ)中獲取session,在一個(gè)新的進(jìn)來(lái)時(shí),將當(dāng)前的 session 設(shè)置到當(dāng)前請(qǐng)求中,在后續(xù)處理過(guò)程如果需要getSession就不需要再去存儲(chǔ)介質(zhì)中再拿一次。
getRequestedSession
這個(gè)是根據(jù)請(qǐng)求信息去取 session
,這里面就包括了 sessionId
解析,從存儲(chǔ)獲取 session
對(duì)象等過(guò)程。
是否創(chuàng)建新的 session
對(duì)象
在當(dāng)前請(qǐng)求中和存儲(chǔ)中都沒(méi)有獲取到 session
信息的情況下,這里會(huì)根據(jù) create
參數(shù)來(lái)判斷是否創(chuàng)建新的 session
。這里一般用戶首次登錄時(shí)或者 session
失效時(shí)會(huì)走到。
1.4 getRequestedSession
根據(jù)請(qǐng)求信息來(lái)獲取 session
對(duì)象
private S getRequestedSession() { // 緩存的請(qǐng)求session是否存在 if (!this.requestedSessionCached) { // 獲取 sessionId List<String> sessionIds = SessionRepositoryFilter.this.httpSessionIdResolver .resolveSessionIds(this); // 通過(guò)sessionId來(lái)從存儲(chǔ)中獲取session for (String sessionId : sessionIds) { if (this.requestedSessionId == null) { this.requestedSessionId = sessionId; } S session = SessionRepositoryFilter.this.sessionRepository .findById(sessionId); if (session != null) { this.requestedSession = session; this.requestedSessionId = sessionId; break; } } this.requestedSessionCached = true; } return this.requestedSession; }
這段代碼還是很有意思的,這里獲取 sessionId
返回的是個(gè)列表。當(dāng)然這里是 SpringSession
的實(shí)現(xiàn)策略,因?yàn)橹С?session
,所以這里以列表的形式返回的。OK,繼續(xù)來(lái)看如何解析 sessionId
的:
這里可以看到 SpringSession
對(duì)于 sessionId
獲取的兩種策略,一種是基于 cookie
,一種是基于 header
;分別來(lái)看下具體實(shí)現(xiàn)。
1.4.1 CookieHttpSessionIdResolver 獲取 sessionId
CookieHttpSessionIdResolver
中獲取 sessionId
的核心代碼如下:
其實(shí)這里沒(méi)啥好說(shuō)的,就是讀 cookie
。從 request
將 cookie
信息拿出來(lái),然后遍歷找當(dāng)前 sessionId
對(duì)應(yīng)的 cookie
,這里的判斷也很簡(jiǎn)單, 如果是以 SESSION
開頭,則表示是 SessionId
,畢竟 cookie
是共享的,不只有 sessionId
,還有可能存儲(chǔ)其他內(nèi)容。
另外這里面有個(gè) jvmRoute,這個(gè)東西實(shí)際上很少能夠用到,因?yàn)榇蠖鄶?shù)情況下這個(gè)值都是null。這個(gè)我們?cè)诜治?CookieSerializer
時(shí)再來(lái)解釋。
1.4.2 HeaderHttpSessionIdResolver 獲取 sessionId
這個(gè)獲取更直接粗暴,就是根據(jù) headerName
從 header
中取值。
回到 getRequestedSession
,剩下的代碼中核心的都是和 sessionRepository
這個(gè)有關(guān)系,這部分就會(huì)涉及到存儲(chǔ)部分。不在本篇的分析范圍之內(nèi),會(huì)在存儲(chǔ)實(shí)現(xiàn)部分來(lái)分析。
1.5 HttpSessionWrapper
上面的代碼中當(dāng)我們拿到 session
實(shí)例是通常會(huì)包裝下,那么用到的就是這個(gè) HttpSessionWrapper
。
HttpSessionWrapper
繼承了 HttpSessionAdapter
,這個(gè) HttpSessionAdapter
就是將SpringSession 轉(zhuǎn)換成一個(gè)標(biāo)準(zhǔn) HttpSession
的適配類。 HttpSessionAdapter
實(shí)現(xiàn)了標(biāo)準(zhǔn) servlet
規(guī)范的 HttpSession
接口。
1.5.1 HttpSessionWrapper
HttpSessionWrapper
重寫了 invalidate
方法。從代碼來(lái)看,調(diào)用該方法產(chǎn)生的影響是:
requestedSessionInvalidated
置為 true
,標(biāo)識(shí)當(dāng)前 session
失效。
將當(dāng)前請(qǐng)求中的 session
設(shè)置為 null
,那么在請(qǐng)求的后續(xù)調(diào)用中通過(guò) getCurrentSession
將拿不到 session
信息。
當(dāng)前緩存的 session 清楚,包括sessionId,session實(shí)例等。
刪除存儲(chǔ)介質(zhì)中的session對(duì)象。
1.5.2 HttpSessionAdapter
SpringSession
和標(biāo)準(zhǔn) HttpSession
的配置器類。這個(gè)怎么理解呢,來(lái)看下一段代碼:
@Override public Object getAttribute(String name) { checkState(); return this.session.getAttribute(name); }
對(duì)于基于容器本身實(shí)現(xiàn)的 HttpSession
來(lái)說(shuō), getAttribute
的實(shí)現(xiàn)也是有容器本身決定。但是這里做了轉(zhuǎn)換之后, getAttribute
將會(huì)通過(guò) SpringSession
中實(shí)現(xiàn)的方案來(lái)獲取。其他的 API
適配也是基于此實(shí)現(xiàn)。
SessionCommittingRequestDispatcher
實(shí)現(xiàn)了 RequestDispatcher
接口。關(guān)于 RequestDispatcher
可以參考這篇文章【Servlet】關(guān)于RequestDispatcher的原理 。 SessionCommittingRequestDispatcher
對(duì) forward
的行為并沒(méi)有改變。 對(duì)于 include
則是在 include
之前提交 session
。為什么這么做呢?
因?yàn)?include
方法使原先的 Servlet
和轉(zhuǎn)發(fā)到的 Servlet
都可以輸出響應(yīng)信息,即原先的 Servlet
還可以繼續(xù)輸出響應(yīng)信息;即請(qǐng)求轉(zhuǎn)發(fā)后,原先的 Servlet
還可以繼續(xù)輸出響應(yīng)信息,轉(zhuǎn)發(fā)到的 Servlet
對(duì)請(qǐng)求做出的響應(yīng)將并入原先 Servlet
的響應(yīng)對(duì)象中。
所以這個(gè)在 include
調(diào)用之前調(diào)用 commit
,這樣可以確保被包含的 Servlet
程序不能改變響應(yīng)消息的狀態(tài)碼和響應(yīng)頭。
2 響應(yīng)重寫
響應(yīng)重寫的目的是確保在請(qǐng)求提交時(shí)能夠把session保存起來(lái)。來(lái)看下 SessionRepositoryResponseWrapper
類的實(shí)現(xiàn):
這里面實(shí)現(xiàn)還就是重寫 onResponseCommitted
,也就是上面說(shuō)的,在請(qǐng)求提交時(shí)能夠通過(guò)這個(gè)回調(diào)函數(shù)將 session
保存到存儲(chǔ)容器中。
2.1 session 提交
最后來(lái)看下 commitSession
這個(gè)過(guò)程不會(huì)再去存儲(chǔ)容器中拿 session
信息,而是直接從當(dāng)前請(qǐng)求中拿。如果拿不到,則在回寫 cookie
時(shí)會(huì)將當(dāng)前 session
對(duì)應(yīng)的 cookie
值設(shè)置為空,這樣下次請(qǐng)求過(guò)來(lái)時(shí)攜帶的 sessionCookie
就是空,這樣就會(huì)重新觸發(fā)登陸。
如果拿到,則清空當(dāng)前請(qǐng)求中的 session
信息,然后將 session
保存到存儲(chǔ)容器中,并且將 sessionId
回寫到 cookie
中。
上述內(nèi)容就是使用SpringSession怎么實(shí)現(xiàn)請(qǐng)求與響應(yīng)重寫,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
文章題目:使用SpringSession怎么實(shí)現(xiàn)請(qǐng)求與響應(yīng)重寫
文章位置:http://www.rwnh.cn/article40/jgpdho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站、移動(dòng)網(wǎng)站建設(shè)、網(wǎng)站改版、企業(yè)網(wǎng)站制作、靜態(tài)網(wǎng)站、網(wǎng)站設(shè)計(jì)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)