日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

【新年好】为什么要 spring-session?

發布時間:2023/12/14 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【新年好】为什么要 spring-session? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

在開始 spring-session 揭秘之前,先做下熱腦(活動活動腦子)運動。主要從以下三個方面進行熱腦:

  • 為什么要 spring-session

  • 比較 traditional-session 方案和 spring-session 方案

  • JSR340 規范與 spring-session 的透明繼承

  • 一. 為什么要 spring-session

    在傳統單機 web 應用中,一般使用 tomcat/jetty 等 web 容器時,用戶的 session 都是由容器管理。瀏覽器使用 cookie 中記錄 sessionId,容器根據 sessionId 判斷用戶是否存在會話 session。這里的限制是,session 存儲在 web 容器中,被單臺服務器容器管理。

    但是網站主鍵演變,分布式應用和集群是趨勢(提高性能)。此時用戶的請求可能被負載分發至不同的服務器,此時傳統的 web 容器管理用戶會話 session 的方式即行不通。除非集群或者分布式 web 應用能夠共享 session,盡管 tomcat 等支持這樣做。但是這樣存在以下兩點問題:

    • 需要侵入 web 容器,提高問題的復雜

    • web 容器之間共享 session,集群機器之間勢必要交互耦合

    基于這些,必須提供新的可靠的集群分布式 / 集群 session 的解決方案,突破 traditional-session 單機限制(即 web 容器 session 方式,下面簡稱 traditional-session),spring-session 應用而生。

    二. 比較 traditional-session 方案和 spring-session 方案

    下圖展示了 traditional-session 和 spring-session 的區別

    傳統模式中,當 request 進入 web 容器,根據 reqest 獲取 session 時,如果 web 容器中存在 session 則返回,如果不存在,web 容器則創建一個 session。然后返回 response 時,將 sessonId 作為 response 的 head 一并返回給客戶端或者瀏覽器。

    但是上節中說明了 traditional-session 的局限性在于:單機 session。在此限制的相反面,即將 session 從 web 容器中抽出來,形成獨立的模塊,以便分布式應用或者集群都能共享,即能解決。

    spring-session 的核心思想在于此:將 session 從 web 容器中剝離,存儲在獨立的存儲服務器中。目前支持多種形式的 session 存儲器:Redis、Database、MogonDB 等。session 的管理責任委托給 spring-session 承擔。當 request 進入 web 容器,根據 request 獲取 session 時,由 spring-session 負責存存儲器中獲取 session,如果存在則返回,如果不存在則創建并持久化至存儲器中。

    三. JSR340 規范與 spring-session 的透明繼承

    JSR340 是 Java Servlet 3.1 的規范提案,其中定義了大量的 api,包括:servlet、servletRequest/HttpServletRequest/HttpServletRequestWrapper、servletResponse/HttpServletResponse/HttpServletResponseWrapper、Filter、Session 等,是標準的 web 容器需要遵循的規約,如 tomcat/jetty/weblogic 等等。

    在日常的應用開發中,develpers 也在頻繁的使用 servlet-api,比如:

    以下的方式獲取請求的 session:

    HttpServletRequest?request?=?... HttpSession?session?=?request.getSession(false);

    其中 HttpServletRequest 和 HttpSession 都是 servlet 規范中定義的接口,web 容器實現的標準。那如果引入 spring-session,要如何獲取 session?

    • 遵循 servlet 規范,同樣方式獲取 session,對應用代碼無侵入且對于 developers 透明化

    • 全新實現一套 session 規范,定義一套新的 api 和 session 管理機制

    兩種方案都可以實現,但是顯然第一種更友好,且具有兼容性。spring-session 正是第一種方案的實現。

    實現第一種方案的關鍵點在于做到透明和兼容

    • 接口適配:仍然使用 HttpServletRequest 獲取 session,獲取到的 session 仍然是 HttpSession 類型——適配器模式

    • 類型包裝增強:Session 不能存儲在 web 容器內,要外化存儲——裝飾模式

    讓人興奮的是,以上的需求在 Servlet 規范中的擴展性都是予以支持!Servlet 規范中定義一系列的接口都是支持擴展,同時提供 Filter 支撐擴展點。建議閱讀《JavaTM Servlet Specification》。

    熱腦活動結束,下面章節正式進入今天的主題:spring-session 揭秘

    Spring Session 探索

    主要從以下兩個方面來說 spring-session:

    • 特點

    • 工作原理

    一. 特點

    spring-session 在無需綁定 web 容器的情況下提供對集群 session 的支持。并提供對以下情況的透明集成:

    • HttpSession:容許替換 web 容器的 HttpSession

    • WebSocket:使用 WebSocket 通信時,提供 Session 的活躍

    • WebSession:容許以應用中立的方式替換 webflux 的 webSession

    二. 工作原理

    再詳細閱讀源碼之前先來看張圖,介紹下 spring-session 中的核心模塊以及之間的交互。

    spring-session 分為以下核心模塊:

    • SessionRepositoryFilter:Servlet 規范中 Filter 的實現,用來切換 HttpSession 至 Spring Session,包裝 HttpServletRequest 和 HttpServletResponse

    • HttpServerletRequest/HttpServletResponse/HttpSessionWrapper 包裝器:包裝原有的 HttpServletRequest、HttpServletResponse 和 Spring Session,實現切換 Session 和透明繼承 HttpSession 的關鍵之所在

    • Session:Spring Session 模塊

    • SessionRepository:管理 Spring Session 的模塊

    • HttpSessionStrategy:映射 HttpRequst 和 HttpResponse 到 Session 的策略

    1. SessionRepositoryFilter

    SessionRepositoryFilter 是一個 Filter 過濾器,符合 Servlet 的規范定義,用來修改包裝請求和響應。這里負責包裝切換 HttpSession 至 Spring Session 的請求和響應。

    @Override protected?void?doFilterInternal(HttpServletRequest?request,HttpServletResponse?response,?FilterChain?filterChain)throws?ServletException,?IOException?{//?設置SessionRepository至Request的屬性中request.setAttribute(SESSION_REPOSITORY_ATTR,?this.sessionRepository);//?包裝原始HttpServletRequest至SessionRepositoryRequestWrapperSessionRepositoryRequestWrapper?wrappedRequest?=?new?SessionRepositoryRequestWrapper(request,?response,?this.servletContext);//?包裝原始HttpServletResponse響應至SessionRepositoryResponseWrapperSessionRepositoryResponseWrapper?wrappedResponse?=?new?SessionRepositoryResponseWrapper(wrappedRequest,?response);//?設置當前請求的HttpSessionStrategy策略HttpServletRequest?strategyRequest?=?this.httpSessionStrategy.wrapRequest(wrappedRequest,?wrappedResponse);//?設置當前響應的HttpSessionStrategy策略HttpServletResponse?strategyResponse?=?this.httpSessionStrategy.wrapResponse(wrappedRequest,?wrappedResponse);try?{filterChain.doFilter(strategyRequest,?strategyResponse);}finally?{//?提交sessionwrappedRequest.commitSession();} }

    以上是 SessionRepositoryFilter 的核心操作,每個 HttpRequest 進入,都會被該 Filter 包裝成切換 Session 的請求很響應對象。

    Tips:責任鏈模式 Filter 是 Servlet 規范中的非常重要的組件,在 tomcat 的實現中使用了責任鏈模式,將多個 Filter 組織成鏈式調用。Filter 的作用就是在業務邏輯執行前后對請求和響應做修改配置。配合 HttpServletRequestWrapper 和 HttpServletResponseWrapper 使用,可謂威力驚人!

    2. SessionRepositoryRequestWrapper

    對于 developers 獲取 HttpSession 的 api

    HttpServletRequest?request?=?...; HttpSession?session?=?request.getSession(true);

    在 spring session 中 request 的實際類型 SessionRepositoryRequestWrapper。調用 SessionRepositoryRequestWrapper 的 getSession 方法會觸發創建 spring session,而非 web 容器的 HttpSession。

    SessionRepositoryRequestWrapper 用來包裝原始的 HttpServletRequest 實現 HttpSession 切換至 Spring Session。是透明 Spring Session 透明集成 HttpSession 的關鍵。

    private?final?class?SessionRepositoryRequestWrapperextends?HttpServletRequestWrapper?{private?final?String?CURRENT_SESSION_ATTR?=?HttpServletRequestWrapper.class.getName();//?當前請求sessionId有效private?Boolean?requestedSessionIdValid;//?當前請求sessionId無效private?boolean?requestedSessionInvalidated;private?final?HttpServletResponse?response;private?final?ServletContext?servletContext;private?SessionRepositoryRequestWrapper(HttpServletRequest?request,HttpServletResponse?response,?ServletContext?servletContext)?{//?調用HttpServletRequestWrapper構造方法,實現包裝super(request);this.response?=?response;this.servletContext?=?servletContext;} }

    SessionRepositoryRequestWrapper 繼承 Servlet 規范中定義的包裝器 HttpServletRequestWrapper。HttpServletRequestWrapper 是 Servlet 規范 api 提供的用于擴展 HttpServletRequest 的擴張點——即裝飾器模式,可以通過重寫一些 api 達到功能點的增強和自定義。

    Tips:裝飾器模式 裝飾器模式(包裝模式)是對功能增強的一種絕佳模式。實際利用的是面向對象的多態性實現擴展。Servlet 規范中開放此 HttpServletRequestWrapper 接口,是讓 developers 自行擴展實現。這種使用方式和 jdk 中的 FilterInputStream/FilterInputStream 如出一轍。

    HttpServletRequestWrapper 中持有一個 HttpServletRequest 對象,然后實現 HttpServletRequest 接口的所有方法,所有方法實現中都是調用持有的 HttpServletRequest 對象的相應的方法。繼承 HttpServletRequestWrapper 可以對其重寫。SessionRepositoryRequestWrapper 繼承 HttpServletRequestWrapper,在構造方法中將原有的 HttpServletRequest 通過調用 super 完成對 HttpServletRequestWrapper 中持有的 HttpServletRequest 初始化賦值,然后重寫和 session 相關的方法。這樣就保證 SessionRepositoryRequestWrapper 的其他方法調用都是使用原有的 HttpServletRequest 的數據,只有 session 相關的是重寫的邏輯。

    Tips:這里的設計是否很精妙!一切都多虧與 Servlet 規范設計的的巧妙啊!

    ”@Override public?HttpSessionWrapper?getSession()?{return?getSession(true); }

    重寫 HttpServletRequest 的 getSession() 方法,調用有參數 getSession(arg) 方法,默認為 true,表示當前 reques 沒有 session 時創建 session。繼續看下有參數 getSession(arg) 的重寫邏輯.

    @Override public?HttpSessionWrapper?getSession(boolean?create)?{//?從當前請求的attribute中獲取session,如果有直接返回HttpSessionWrapper?currentSession?=?getCurrentSession();if?(currentSession?!=?null)?{return?currentSession;}//?獲取當前request的sessionId,這里使用了HttpSessionStrategy//?決定怎樣將Request映射至Session,默認使用Cookie策略,即從cookies中解析sessionIdString?requestedSessionId?=?getRequestedSessionId();//?請求的如果sessionId存在且當前request的attribute中的沒有session失效屬性//?則根據sessionId獲取spring?sessionif?(requestedSessionId?!=?null&&?getAttribute(INVALID_SESSION_ID_ATTR)?==?null)?{S?session?=?getSession(requestedSessionId);//?如果spring?session不為空,則將spring?session包裝成HttpSession并//?設置到當前Request的attribute中,防止同一個request?getsession時頻繁的到存儲器//中獲取session,提高性能if?(session?!=?null)?{this.requestedSessionIdValid?=?true;currentSession?=?new?HttpSessionWrapper(session,?getServletContext());currentSession.setNew(false);setCurrentSession(currentSession);return?currentSession;}//?如果根據sessionId,沒有獲取到session,則設置當前request屬性,此sessionId無效//?同一個請求中獲取session,直接返回無效else?{//?This?is?an?invalid?session?id.?No?need?to?ask?again?if//?request.getSession?is?invoked?for?the?duration?of?this?requestif?(SESSION_LOGGER.isDebugEnabled())?{SESSION_LOGGER.debug("No?session?found?by?id:?Caching?result?for?getSession(false)?for?this?HttpServletRequest.");}setAttribute(INVALID_SESSION_ID_ATTR,?"true");}}//?判斷是否創建sessionif?(!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)"));}//?根據sessionRepository創建spring?sessionS?session?=?SessionRepositoryFilter.this.sessionRepository.createSession();//?設置session的最新訪問時間session.setLastAccessedTime(System.currentTimeMillis());//?包裝成HttpSession透明化集成currentSession?=?new?HttpSessionWrapper(session,?getServletContext());//?設置session至Requset的attribute中,提高同一個request訪問session的性能setCurrentSession(currentSession);return?currentSession; }

    再來看下 spring session 的持久化。上述 SessionRepositoryFilter 在包裝 HttpServletRequest 后,執行 FilterChain 中使用 finally 保證請求的 Session 始終 session 會被提交,此提交操作中將 sesionId 設置到 response 的 head 中并將 session 持久化至存儲器中。

    持久化只持久 spring session,并不是將 spring session 包裝后的 HttpSession 持久化,因為 HttpSession 不過是包裝器,持久化沒有意義。

    /***?Uses?the?HttpSessionStrategy?to?write?the?session?id?to?the?response?and*?persist?the?Session.*/ private?void?commitSession()?{//?獲取當前sessionHttpSessionWrapper?wrappedSession?=?getCurrentSession();//?如果當前session為空,則刪除cookie中的相應的sessionIdif?(wrappedSession?==?null)?{if?(isInvalidateClientSession())?{SessionRepositoryFilter.this.httpSessionStrategy.onInvalidateSession(this,?this.response);}}else?{//?從HttpSession中獲取當前spring?sessionS?session?=?wrappedSession.getSession();//?持久化spring?session至存儲器SessionRepositoryFilter.this.sessionRepository.save(session);//?如果是新創建spring?session,sessionId到response的cookieif?(!isRequestedSessionIdValid()||?!session.getId().equals(getRequestedSessionId()))?{SessionRepositoryFilter.this.httpSessionStrategy.onNewSession(session,this,?this.response);}} }

    再來看下包裝的響應 SessionRepositoryResponseWrapper。

    3.SessionRepositoryResponseWrapper

    /***?Allows?ensuring?that?the?session?is?saved?if?the?response?is?committed.**?@author?Rob?Winch*?@since?1.0*/ private?final?class?SessionRepositoryResponseWrapperextends?OnCommittedResponseWrapper?{private?final?SessionRepositoryRequestWrapper?request;/***?Create?a?new?{@link?SessionRepositoryResponseWrapper}.*?@param?request?the?request?to?be?wrapped*?@param?response?the?response?to?be?wrapped*/SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper?request,HttpServletResponse?response)?{super(response);if?(request?==?null)?{throw?new?IllegalArgumentException("request?cannot?be?null");}this.request?=?request;}@Overrideprotected?void?onResponseCommitted()?{this.request.commitSession();} }

    上面的注釋已經非常詳細,這里不再贅述。這里只講述為什么需要包裝原始的響應。從注釋上可以看出包裝響應時為了:確保如果響應被提交 session 能夠被保存。

    這里我有點疑惑:在上述的 SessionRepositoryFilter.doFilterInternal 方法中不是已經 request.commitSession() 了嗎,FilterChain 執行完或者異常后都會執行 Finally 中的 request.commitSession。為什么這里仍然需要包裝響應,為了確保 session 能夠保存,包裝器中的 onResponseCommitted 方法可以看出也是做了一次 request.commitSession()。難道這不是多此一舉?

    Tips 如果有和我相同疑問的同學,那就說明我們的基礎都不扎實,對 Servlet 仍然沒有一個清楚全面的認識。對于此問題,我特意在 github 上提了 issuse:Why is the request.commitSession() method called repeatedly?。

    但是在提完 issue 后的回家路上,我思考了下 response 可以有流方式的寫,會不會在 response.getOutStream 寫的時候已經將響應全部返回到客戶端,這時響應結束。

    在家中是,spring sesion 作者大大已經回復了我的 issue:

    Is this causing you problems? The reason is that we need to ensure that the session is created before the response is committed. If the response is already committed there will be no way to track the session (i.e. a cookie cannot be written to the response to keep track of which session id).

    他的意思是:我們需要在 response 被提交之前確保 session 被創建。如果 response 已經被提交,將沒有辦法追蹤 session(例如:無法將 cookie 寫入 response 以跟蹤哪個 session id)。

    在此之前我又閱讀了 JavaTM Servlet Specification,規范中這樣解釋 Response 的 flushBuffer 接口:

    The isCommitted method returns a boolean value indicating whether any response bytes have been returned to the client. The flushBuffer method forces content in the buffer to be written to the client.

    并且看了 ServletResponse 的 flushBuffer 的 javadocs:

    /***?Forces?any?content?in?the?buffer?to?be?written?to?the?client.?A?call?to*?this?method?automatically?commits?the?response,?meaning?the?status?code*?and?headers?will?be?written.**?@throws?IOException?if?an?I/O?occurs?during?the?flushing?of?the?response**?@see?#setBufferSize*?@see?#getBufferSize*?@see?#isCommitted*?@see?#reset*/ public?void?flushBuffer()?throws?IOException;

    結合以上兩點,一旦 response 執行 flushBuffer 方法,迫使 Response 中在 Buffer 中任何數據都會被返回至 client 端。這個方法自動提交響應中的 status code 和 head。那么如果不包裝請求,監聽 flushBuffer 事件在提交 response 前,將 session 寫入 response 和持久化 session,將導致作者大大說的無法追蹤 session。

    SessionRepositoryResponseWrapper 繼承父類 OnCommittedResponseWrapper,其中 flushBuffer 方法如下:

    /***?Makes?sure?{@link?OnCommittedResponseWrapper#onResponseCommitted()}?is?invoked*?before?calling?the?superclass?<code>flushBuffer()</code>.*?@throws?IOException?if?an?input?or?output?exception?occurred*/ @Override public?void?flushBuffer()?throws?IOException?{doOnResponseCommitted();super.flushBuffer(); }/***?Calls?<code>onResponseCommmitted()</code>?with?the?current?contents?as?long?as*?{@link?#disableOnResponseCommitted()}?was?not?invoked.*/ private?void?doOnResponseCommitted()?{if?(!this.disableOnCommitted)?{onResponseCommitted();disableOnResponseCommitted();} }

    重寫 HttpServletResponse 方法,監聽 response commit,當發生 response commit 時,可以在 commit 之前寫 session 至 response 中并持久化 session。

    Tips: spring mvc 中 HttpMessageConverters 使用到的 jackson 即調用了 outstream.flushBuffer(),當使用 @ResponseBody 時。

    以上做法固然合理,但是如此重復操作兩次 commit,存在兩次 persist session? 這個問題后面涉及 SessionRepository 時再詳述!

    再看 SessionRepository 之前,先來看下 spring session 中的 session 接口。

    3.Session 接口

    spring-session 和 tomcat 中的 Session 的實現模式上有很大不同,tomcat 中直接對 HttpSession 接口進行實現,而 spring-session 中則抽象出單獨的 Session 層接口,讓后再使用適配器模式將 Session 適配層 Servlet 規范中的 HttpSession。spring-sesion 中關于 session 的實現和適配整個 UML 類圖如下:

    Tips:適配器模式 spring-session 單獨抽象出 Session 層接口,可以應對多種場景下不同的 session 的實現,然后通過適配器模式將 Session 適配成 HttpSession 的接口,精妙至極!

    Session 是 spring-session 對 session 的抽象,主要是為了鑒定用戶,為 Http 請求和響應提供上下文過程,該 Session 可以被 HttpSession、WebSocket Session,非 WebSession 等使用。定義了 Session 的基本行為:

    • getId:獲取 sessionId

    • setAttribute:設置 session 屬性

    • getAttribte:獲取 session 屬性

    ExipringSession:提供 Session 額外的過期特性。定義了以下關于過期的行為:

    • setLastAccessedTime:設置最近 Session 會話過程中最近的訪問時間

    • getLastAccessedTime:獲取最近的訪問時間

    • setMaxInactiveIntervalInSeconds:設置 Session 的最大閑置時間

    • getMaxInactiveIntervalInSeconds:獲取最大閑置時間

    • isExpired:判斷 Session 是否過期

    MapSession:基于 java.util.Map 的 ExpiringSession 的實現

    RedisSession:基于 MapSession 和 Redis 的 ExpiringSession 實現,提供 Session 的持久化能力

    先來看下 MapSession 的代碼源碼片段

    public?final?class?MapSession?implements?ExpiringSession,?Serializable?{/***?Default?{@link?#setMaxInactiveIntervalInSeconds(int)}?(30?minutes).*/public?static?final?int?DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS?=?1800;private?String?id;private?Map<String,?Object>?sessionAttrs?=?new?HashMap<String,?Object>();private?long?creationTime?=?System.currentTimeMillis();private?long?lastAccessedTime?=?this.creationTime;/***?Defaults?to?30?minutes.*/private?int?maxInactiveInterval?=?DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;

    MapSession 中持有 HashMap 類型的變量 sessionAtts 用于存儲 Session 設置屬性,比如調用的 setAttribute 方法的 k-v 就存儲在該 HashMap 中。這個和 tomcat 內部實現 HttpSession 的方式類似,tomcat 中使用了 ConcurrentHashMap 存儲。

    其中 lastAccessedTime 用于記錄最近的一次訪問時間,maxInactiveInterval 用于記錄 Session 的最大閑置時間(過期時間 - 針對沒有 Request 活躍的情況下的最大時間,即相對于最近一次訪問后的最大閑置時間)。

    public?void?setAttribute(String?attributeName,?Object?attributeValue)?{if?(attributeValue?==?null)?{removeAttribute(attributeName);}else?{this.sessionAttrs.put(attributeName,?attributeValue);} }

    setAttribute 方法極其簡單,null 時就移除 attributeName,否則 put 存儲。

    重點熟悉 RedisSession 如何實現 Session 的行為:setAttribute、persistence 等。

    /***?A?custom?implementation?of?{@link?Session}?that?uses?a?{@link?MapSession}?as?the*?basis?for?its?mapping.?It?keeps?track?of?any?attributes?that?have?changed.?When*?{@link?org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession#saveDelta()}*?is?invoked?all?the?attributes?that?have?been?changed?will?be?persisted.**?@author?Rob?Winch*?@since?1.0*/ final?class?RedisSession?implements?ExpiringSession?{private?final?MapSession?cached;private?Long?originalLastAccessTime;private?Map<String,?Object>?delta?=?new?HashMap<String,?Object>();private?boolean?isNew;private?String?originalPrincipalName;

    首先看 javadocs,對于閱讀源碼,學會看 javadocs 非常重要!

    基于 MapSession 的基本映射實現的 Session,能夠追蹤發生變化的所有屬性,當調用 saveDelta 方法后,變化的屬性將被持久化!

    在 RedisSession 中有兩個非常重要的成員屬性:

    • cached:實際上是一個 MapSession 實例,用于做本地緩存,每次在 getAttribute 時無需從 Redis 中獲取,主要為了 improve 性能

    • delta:用于跟蹤變化數據,做持久化

    再來看下 RedisSession 中最為重要的行為 saveDelta——持久化 Session 至 Redis 中:

    /***?Saves?any?attributes?that?have?been?changed?and?updates?the?expiration?of?this*?session.*/ private?void?saveDelta()?{//?如果delta為空,則Session中沒有任何數據需要存儲if?(this.delta.isEmpty())?{return;}String?sessionId?=?getId();//?使用spring?data?redis將delta中的數據保存至Redis中getSessionBoundHashOperations(sessionId).putAll(this.delta);String?principalSessionKey?=?getSessionAttrNameKey(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);String?securityPrincipalSessionKey?=?getSessionAttrNameKey(SPRING_SECURITY_CONTEXT);if?(this.delta.containsKey(principalSessionKey)||?this.delta.containsKey(securityPrincipalSessionKey))?{if?(this.originalPrincipalName?!=?null)?{String?originalPrincipalRedisKey?=?getPrincipalKey(this.originalPrincipalName);RedisOperationsSessionRepository.this.sessionRedisOperations.boundSetOps(originalPrincipalRedisKey).remove(sessionId);}String?principal?=?PRINCIPAL_NAME_RESOLVER.resolvePrincipal(this);this.originalPrincipalName?=?principal;if?(principal?!=?null)?{String?principalRedisKey?=?getPrincipalKey(principal);RedisOperationsSessionRepository.this.sessionRedisOperations.boundSetOps(principalRedisKey).add(sessionId);}}?//?清空delta,代表沒有任何需要持久化的數據。同時保證//SessionRepositoryFilter和SessionRepositoryResponseWrapper的onResponseCommitted//只會持久化一次Session至Redis中,解決前面提到的疑問this.delta?=?new?HashMap<String,?Object>(this.delta.size());??//?更新過期時間,滾動至下一個過期時間間隔的時刻Long?originalExpiration?=?this.originalLastAccessTime?==?null???null:?this.originalLastAccessTime?+?TimeUnit.SECONDS.toMillis(getMaxInactiveIntervalInSeconds());RedisOperationsSessionRepository.this.expirationPolicy.onExpirationUpdated(originalExpiration,?this); }

    從 javadoc 中可以看出,saveDelta 用于存儲 Session 的屬性:

  • 保存 Session 中的屬性數據至 Redis 中

  • 清空 delta 中數據,防止重復提交 Session 中的數據

  • 更新過期時間至下一個過期時間間隔的時刻

  • 再看下 RedisSession 中的其他行為

    //?設置session的存活時間,即最大過期時間。先保存至本地緩存,然后再保存至delta public?void?setMaxInactiveIntervalInSeconds(int?interval)?{this.cached.setMaxInactiveIntervalInSeconds(interval);this.delta.put(MAX_INACTIVE_ATTR,?getMaxInactiveIntervalInSeconds());flushImmediateIfNecessary(); }//?直接從本地緩存獲取過期時間 public?int?getMaxInactiveIntervalInSeconds()?{return?this.cached.getMaxInactiveIntervalInSeconds(); }//?直接從本地緩存中獲取Session中的屬性 @SuppressWarnings("unchecked") public?Object?getAttribute(String?attributeName)?{return?this.cached.getAttribute(attributeName); }//?保存Session屬性至本地緩存和delta中 public?void?setAttribute(String?attributeName,?Object?attributeValue)?{this.cached.setAttribute(attributeName,?attributeValue);this.delta.put(getSessionAttrNameKey(attributeName),?attributeValue);flushImmediateIfNecessary(); }

    除了 MapSession 和 RedisSession 還有 JdbcSession、MongoExpiringSession,感興趣的讀者可以自行閱讀。

    下面看 SessionRepository 的邏輯。SessionRepository 是 spring session 中用于管理 spring session 的核心組件。

    4. SessionRepository

    A repository interface for managing {@link Session} instances.

    javadoc 中描述 SessionRepository 為管理 spring-session 的接口實例。抽象出:

    S?createSession(); void?save(S?session); S?getSession(String?id); void?delete(String?id);

    創建、保存、獲取、刪除 Session 的接口行為。根據 Session 的不同,分為很多種 Session 操作倉庫。

    這里重點介紹下 RedisOperationsSessionRepository。在詳細介紹其之前,了解下 RedisOperationsSessionRepository 的數據存儲細節。

    當創建一個 RedisSession,然后存儲在 Redis 中時,RedisSession 的存儲細節如下:

    spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe spring:session:expirations:1439245080000

    Redis 會為每個 RedisSession 存儲三個 k-v。

  • 第一個 k-v 用來存儲 Session 的詳細信息,包括 Session 的過期時間間隔、最近的訪問時間、attributes 等等。這個 k 的過期時間為 Session 的最大過期時間 + 5 分鐘。如果默認的最大過期時間為 30 分鐘,則這個 k 的過期時間為 35 分鐘

  • 第二個 k-v 用來表示 Session 在 Redis 中的過期,這個 k-v 不存儲任何有用數據,只是表示 Session 過期而設置。這個 k 在 Redis 中的過期時間即為 Session 的過期時間間隔

  • 第三個 k-v 存儲這個 Session 的 id,是一個 Set 類型的 Redis 數據結構。這個 k 中的最后的 1439245080000 值是一個時間戳,根據這個 Session 過期時刻滾動至下一分鐘而計算得出。

  • 這里不由好奇,為什么一個 RedisSession 卻如此復雜的存儲。關于這個可以參考 spring-session 作者本人在 github 上的兩篇回答:

    Why does Spring Session use spring:session:expirations?

    Clarify Redis expirations and cleanup task

    簡單描述下,為什么 RedisSession 的存儲用到了三個 Key,而非一個 Redis 過期 Key。對于 Session 的實現,需要支持 HttpSessionEvent,即 Session 創建、過期、銷毀等事件。當應用用監聽器設置監聽相應事件,Session 發生上述行為時,監聽器能夠做出相應的處理。Redis 的強大之處在于支持 KeySpace Notifiction——鍵空間通知。即可以監視某個 key 的變化,如刪除、更新、過期。當 key 發生上述行為是,以便可以接受到變化的通知做出相應的處理。具體詳情可以參考:Redis Keyspace Notifications

    但是 Redis 中帶有過期的 key 有兩種方式:

    • 當訪問時發現其過期

    • Redis 后臺逐步查找過期鍵

    當訪問時發現其過期,會產生過期事件,但是無法保證 key 的過期時間抵達后立即生成過期事件。具體可以參考:Timing of expired events

    spring-session 為了能夠及時的產生 Session 的過期時的過期事件,所以增加了:

    spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe spring:session:expirations:1439245080000

    spring-session 中有個定時任務,每個整分鐘都會查詢相應的 spring:session:expirations: 整分鐘的時間戳中的過期 SessionId,然后再訪問一次這個 SessionId,即 spring:session:sessions:expires:SessionId,以便能夠讓 Redis 及時的產生 key 過期事件——即 Session 過期事件。

    接下來再看下 RedisOperationsSessionRepository 中的具體實現原理

    createSession 方法:

    public?RedisSession?createSession()?{//?new一個RedisSession實例RedisSession?redisSession?=?new?RedisSession();//?如果設置的最大過期時間不為空,則設置RedisSession的過期時間if?(this.defaultMaxInactiveInterval?!=?null)?{redisSession.setMaxInactiveIntervalInSeconds(this.defaultMaxInactiveInterval);}return?redisSession; }

    再來看下 RedisSession 的構造方法:

    /***?Creates?a?new?instance?ensuring?to?mark?all?of?the?new?attributes?to?be*?persisted?in?the?next?save?operation.*/ RedisSession()?{//?設置本地緩存為MapSessionthis(new?MapSession());//?設置Session的基本屬性this.delta.put(CREATION_TIME_ATTR,?getCreationTime());this.delta.put(MAX_INACTIVE_ATTR,?getMaxInactiveIntervalInSeconds());this.delta.put(LAST_ACCESSED_ATTR,?getLastAccessedTime());//?標記Session的是否為新創建this.isNew?=?true;//?持久化flushImmediateIfNecessary(); }

    save 方法:

    public?void?save(RedisSession?session)?{//?調用RedisSession的saveDelta持久化Sessionsession.saveDelta();//?如果Session為新創建,則發布一個Session創建的事件if?(session.isNew())?{String?sessionCreatedKey?=?getSessionCreatedChannel(session.getId());this.sessionRedisOperations.convertAndSend(sessionCreatedKey,?session.delta);session.setNew(false);} }

    getSession 方法:

    //?根據SessionId獲取Session,這里的false代表的參數 //?指:如果Session已經過期,是否仍然獲取返回 public?RedisSession?getSession(String?id)?{return?getSession(id,?false); }

    在有些情況下,Session 過期,仍然需要能夠獲取到 Session。這里先來看下 getSession(String id, boolean allowExpired):

    private?RedisSession?getSession(String?id,?boolean?allowExpired)?{//?根據SessionId,從Redis獲取到持久化的Session信息Map<Object,?Object>?entries?=?getSessionBoundHashOperations(id).entries();//?如果Redis中沒有,則返回nullif?(entries.isEmpty())?{return?null;}//?根據Session信息,加載創建一個MapSession對象MapSession?loaded?=?loadSession(id,?entries);//??判斷是否允許過期獲取和Session是否過期if?(!allowExpired?&&?loaded.isExpired())?{return?null;}//?根據MapSession?new一個信息的RedisSession,此時isNew為falseRedisSession?result?=?new?RedisSession(loaded);//?設置最新的訪問時間result.originalLastAccessTime?=?loaded.getLastAccessedTime();return?result; }

    這里需要注意的是 loaded.isExpired() 和 loadSession。loaded.isExpired 判斷 Session 是否過期,如果過期返回 null:

    public?boolean?isExpired()?{//?根據當前時間判斷是否過期return?isExpired(System.currentTimeMillis()); } boolean?isExpired(long?now)?{//?如果maxInactiveInterval小于0,表示Session永不過期if?(this.maxInactiveInterval?<?0)?{return?false;}//?最大過期時間單位轉換為毫秒//?當前時間減去Session的最大有效期間隔以獲取理論上有效的上一次訪問時間//?然后在與實際的上一次訪問時間進行比較//?如果大于,表示理論上的時間已經在實際的訪問時間之后,那么表示Session已經過期return?now?-?TimeUnit.SECONDS.toMillis(this.maxInactiveInterval)?>=?this.lastAccessedTime; }

    loadSession 中,將 Redis 中存儲的 Session 信息轉換為 MapSession 對象,以便從 Session 中獲取屬性時能夠從內存直接獲取提高性能:

    private?MapSession?loadSession(String?id,?Map<Object,?Object>?entries)?{MapSession?loaded?=?new?MapSession(id);for?(Map.Entry<Object,?Object>?entry?:?entries.entrySet())?{String?key?=?(String)?entry.getKey();if?(CREATION_TIME_ATTR.equals(key))?{loaded.setCreationTime((Long)?entry.getValue());}else?if?(MAX_INACTIVE_ATTR.equals(key))?{loaded.setMaxInactiveIntervalInSeconds((Integer)?entry.getValue());}else?if?(LAST_ACCESSED_ATTR.equals(key))?{loaded.setLastAccessedTime((Long)?entry.getValue());}else?if?(key.startsWith(SESSION_ATTR_PREFIX))?{loaded.setAttribute(key.substring(SESSION_ATTR_PREFIX.length()),entry.getValue());}}return?loaded; }

    至此,可以看出 spring-session 中 request.getSession(false) 的過期實現原理。

    delete 方法:

    public?void?delete(String?sessionId)?{//?獲取SessionRedisSession?session?=?getSession(sessionId,?true);if?(session?==?null)?{return;}cleanupPrincipalIndex(session);//?從過期集合中移除sessionIdthis.expirationPolicy.onDelete(session);String?expireKey?=?getExpiredKey(session.getId());//?刪除session的過期鍵this.sessionRedisOperations.delete(expireKey);//?設置session過期session.setMaxInactiveIntervalInSeconds(0);save(session); }

    至此 RedisOperationsSessionRepository 的核心原理就介紹完畢。但是 RedisOperationsSessionRepository 中還包括關于 Session 事件的處理和清理 Session 的定時任務。這部分內容在后述的 SessionEvent 部分介紹。

    5. HttpSessionStrategy

    A strategy for mapping HTTP request and responses to a {@link Session}.

    從 javadoc 中可以看出,HttpSessionStrategy 是建立 Request/Response 和 Session 之間的映射關系的策略。

    Tips:策略模式 策略模式是一個傳神的神奇模式,是 java 的多態非常典型應用,是開閉原則、迪米特法則的具體體現。將同類型的一系列的算法封裝在不同的類中,通過使用接口注入不同類型的實現,以達到的高擴展的目的。一般是定義一個策略接口,按照不同的場景實現各自的策略。

    該策略接口中定義一套策略行為:

    //?根據請求獲取SessionId,即建立請求至Session的映射關系 String?getRequestedSessionId(HttpServletRequest?request); //?對于新創建的Session,通知客戶端 void?onNewSession(Session?session,?HttpServletRequest?request,HttpServletResponse?response); //?對于session無效,通知客戶端 void?onInvalidateSession(HttpServletRequest?request,?HttpServletResponse?response);

    如下 UML 類圖:

    這里主要介紹 CookieHttpSessionStrategy,這個也是默認的策略,可以查看 spring-session 中類 SpringHttpSessionConfiguration,在注冊 SessionRepositoryFilter Bean 時默認采用 CookieHttpSessionStrategy:

    @Bean public?<S?extends?ExpiringSession>?SessionRepositoryFilter<??extends?ExpiringSession>?springSessionRepositoryFilter(SessionRepository<S>?sessionRepository)?{SessionRepositoryFilter<S>?sessionRepositoryFilter?=?new?SessionRepositoryFilter<S>(sessionRepository);sessionRepositoryFilter.setServletContext(this.servletContext);if?(this.httpSessionStrategy?instanceof?MultiHttpSessionStrategy)?{sessionRepositoryFilter.setHttpSessionStrategy((MultiHttpSessionStrategy)?this.httpSessionStrategy);}else?{sessionRepositoryFilter.setHttpSessionStrategy(this.httpSessionStrategy);}return?sessionRepositoryFilter; }

    下面來分析 CookieHttpSessionStrategy 的原理。該策略使用 Cookie 來映射 Request/Response 至 Session。即 request/requset 的 head 中 cookie 存儲 SessionId,當請求至 web 服務器,可以解析請求 head 中的 cookie,然后獲取 sessionId,根據 sessionId 獲取 spring-session。當創建新的 session 或者 session 過期,將相應的 sessionId 寫入 response 的 set-cookie 或者從 respose 中移除 sessionId。

    getRequestedSessionId 方法

    public?String?getRequestedSessionId(HttpServletRequest?request)?{//?獲取當前請求的sessionId:session別名和sessionId映射Map<String,?String>?sessionIds?=?getSessionIds(request);//?獲取當前請求的Session別名String?sessionAlias?=?getCurrentSessionAlias(request);//?獲取相應別名的sessionIdreturn?sessionIds.get(sessionAlias); }

    接下來看下具體獲取 SessionIds 的具體過程:

    public?String?getRequestedSessionId(HttpServletRequest?request)?{//?獲取當前請求的sessionId:session別名和sessionId映射Map<String,?String>?sessionIds?=?getSessionIds(request);//?獲取當前請求的Session別名String?sessionAlias?=?getCurrentSessionAlias(request);//?獲取相應別名的sessionIdreturn?sessionIds.get(sessionAlias); }public?Map<String,?String>?getSessionIds(HttpServletRequest?request)?{//?解析request中的cookie值List<String>?cookieValues?=?this.cookieSerializer.readCookieValues(request);//?獲取sessionIdString?sessionCookieValue?=?cookieValues.isEmpty()???"":?cookieValues.iterator().next();Map<String,?String>?result?=?new?LinkedHashMap<String,?String>();//?根據分詞器對sessionId進行分割,因為spring-session支持多session。默認情況只有一個sessionStringTokenizer?tokens?=?new?StringTokenizer(sessionCookieValue,?this.deserializationDelimiter);//?如果只有一個session,則設置默認別名為0if?(tokens.countTokens()?==?1)?{result.put(DEFAULT_ALIAS,?tokens.nextToken());return?result;}//?如果有多個session,則建立別名和sessionId的映射while?(tokens.hasMoreTokens())?{String?alias?=?tokens.nextToken();if?(!tokens.hasMoreTokens())?{break;}String?id?=?tokens.nextToken();result.put(alias,?id);}return?result; }public?List<String>?readCookieValues(HttpServletRequest?request)?{//?獲取request的cookieCookie[]?cookies?=?request.getCookies();List<String>?matchingCookieValues?=?new?ArrayList<String>();if?(cookies?!=?null)?{for?(Cookie?cookie?:?cookies)?{//?如果是以SESSION開頭,則表示是SessionId,畢竟cookie不只有sessionId,還有可能存儲其他內容if?(this.cookieName.equals(cookie.getName()))?{//?決策是否需要base64?decodeString?sessionId?=?this.useBase64Encoding??base64Decode(cookie.getValue())?:?cookie.getValue();if?(sessionId?==?null)?{continue;}if?(this.jvmRoute?!=?null?&&?sessionId.endsWith(this.jvmRoute))?{sessionId?=?sessionId.substring(0,sessionId.length()?-?this.jvmRoute.length());}//?存入list中matchingCookieValues.add(sessionId);}}}return?matchingCookieValues; }

    再來看下獲取當前 request 對應的 Session 的別名方法 getCurrentSessionAlias

    public?String?getCurrentSessionAlias(HttpServletRequest?request)?{//?如果session參數為空,則返回默認session別名if?(this.sessionParam?==?null)?{return?DEFAULT_ALIAS;}//?從request中獲取session別名,如果為空則返回默認別名String?u?=?request.getParameter(this.sessionParam);if?(u?==?null)?{return?DEFAULT_ALIAS;}if?(!ALIAS_PATTERN.matcher(u).matches())?{return?DEFAULT_ALIAS;}return?u; }

    spring-session 為了支持多 session,才弄出多個 session 別名。當時一般應用場景都是一個 session,都是默認的 session 別名 0。

    上述獲取 sessionId 和別名映射關系中,也是默認別名 0。這里返回別名 0,所以返回當前請求對應的 sessionId。

    onNewSession 方法

    public?void?onNewSession(Session?session,?HttpServletRequest?request,HttpServletResponse?response)?{//?從當前request中獲取已經寫入Cookie的sessionId集合Set<String>?sessionIdsWritten?=?getSessionIdsWritten(request);//?判斷是否包含,如果包含,表示該sessionId已經寫入過cookie中,則直接返回if?(sessionIdsWritten.contains(session.getId()))?{return;}//?如果沒有寫入,則加入集合,后續再寫入sessionIdsWritten.add(session.getId());Map<String,?String>?sessionIds?=?getSessionIds(request);String?sessionAlias?=?getCurrentSessionAlias(request);sessionIds.put(sessionAlias,?session.getId());//?獲取cookieValueString?cookieValue?=?createSessionCookieValue(sessionIds);//將cookieValue寫入Cookie中this.cookieSerializer.writeCookieValue(new?CookieValue(request,?response,?cookieValue)); }

    sessionIdsWritten 主要是用來記錄已經寫入 Cookie 的 SessionId,防止 SessionId 重復寫入 Cookie 中。

    onInvalidateSession 方法

    public?void?onInvalidateSession(HttpServletRequest?request,HttpServletResponse?response)?{//?從當前request中獲取sessionId和別名映射Map<String,?String>?sessionIds?=?getSessionIds(request);//?獲取別名String?requestedAlias?=?getCurrentSessionAlias(request);//?移除sessionIdsessionIds.remove(requestedAlias);String?cookieValue?=?createSessionCookieValue(sessionIds);//?寫入移除后的sessionIdthis.cookieSerializer.writeCookieValue(new?CookieValue(request,?response,?cookieValue)); }

    繼續看下具體的寫入 writeCookieValue 原理:

    public?void?writeCookieValue(CookieValue?cookieValue)?{//?獲取request/respose和cookie值HttpServletRequest?request?=?cookieValue.getRequest();HttpServletResponse?response?=?cookieValue.getResponse();String?requestedCookieValue?=?cookieValue.getCookieValue();String?actualCookieValue?=?this.jvmRoute?==?null???requestedCookieValue:?requestedCookieValue?+?this.jvmRoute;//?構造servlet規范中的Cookie對象,注意這里cookieName為:SESSION,表示為Session,//?上述的從Cookie中讀取SessionId,也是使用該cookieNameCookie?sessionCookie?=?new?Cookie(this.cookieName,?this.useBase64Encoding??base64Encode(actualCookieValue)?:?actualCookieValue);//?設置cookie的屬性:secure、path、domain、httpOnlysessionCookie.setSecure(isSecureCookie(request));sessionCookie.setPath(getCookiePath(request));String?domainName?=?getDomainName(request);if?(domainName?!=?null)?{sessionCookie.setDomain(domainName);}if?(this.useHttpOnlyCookie)?{sessionCookie.setHttpOnly(true);}//?如果cookie值為空,則失效if?("".equals(requestedCookieValue))?{sessionCookie.setMaxAge(0);}else?{sessionCookie.setMaxAge(this.cookieMaxAge);}//?寫入cookie到response中response.addCookie(sessionCookie); }

    至此,CookieHttpSessionStrategy 介紹結束。

    由于篇幅過長,關于 spring-session event 和 RedisOperationSessionRepository 清理 session 并且產生過期事件的部分后續文章介紹。

    總結

    spring-session 提供集群環境下 HttpSession 的透明集成。spring-session 的優勢在于開箱即用,具有較強的設計模式。且支持多種持久化方式,其中 RedisSession 較為成熟,與 spring-data-redis 整合,可謂威力無窮。


    作者:懷瑾握瑜

    來源鏈接:

    https://www.cnblogs.com/lxyit/p/9672097.html

    總結

    以上是生活随笔為你收集整理的【新年好】为什么要 spring-session?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    免费一级特黄录像 | 免费在线观看黄色网 | 91精品视频导航 | 日韩一级成人av | 久久不卡日韩美女 | 久久草草热国产精品直播 | 国产免费叼嘿网站免费 | 日韩毛片精品 | 国产精品成人一区二区三区 | 亚洲精品欧洲精品 | 国产日韩欧美自拍 | 久久成人18免费网站 | 天天干夜夜擦 | 韩国av电影在线观看 | 久久国产香蕉视频 | 成人av直播| 日韩在线视频一区二区三区 | av中文字幕在线免费观看 | 国产精品一区二区在线播放 | 亚洲va欧美va人人爽春色影视 | 国产福利av| 久久久久久久久久福利 | 国产精品亚洲人在线观看 | 色综合色综合久久综合频道88 | 最新免费中文字幕 | 啪啪小视频网站 | 欧洲精品码一区二区三区免费看 | av在线免费在线 | 香蕉久草 | 免费在线观看一区 | 日日夜夜艹 | 香蕉视频在线播放 | 国产在线观看91 | 日韩精品在线免费播放 | 久久影视网 | 中文字幕一区av | 999在线观看视频 | 国产伦精品一区二区三区在线 | 精品久久久久一区二区国产 | 国产又粗又猛又爽又黄的视频免费 | 涩av在线| 综合色站导航 | 中文字幕av免费在线观看 | 中文字幕一区二区三区四区 | 91视频链接 | 日本黄色免费网站 | 五月婷婷在线视频 | 成年人黄色免费视频 | 中文字幕专区高清在线观看 | 中文字幕资源在线 | 在线日韩精品视频 | 国产在线播放一区二区三区 | 免费亚洲视频在线观看 | 亚洲黄色在线观看 | 久久久久国产一区二区三区 | 探花视频在线观看免费 | 日韩免费在线观看 | 日韩午夜av电影 | 亚洲视频 在线观看 | 日韩欧美成 | 色综合久久中文综合久久牛 | 国产亚洲综合在线 | 午夜精品久久久久久久久久久久 | 视频一区二区三区视频 | 国产亚洲在线观看 | 丰满少妇在线观看资源站 | 成人精品一区二区三区中文字幕 | 亚洲一区二区麻豆 | 午夜精选视频 | 色综合天天干 | 超碰人人射 | 在线探花 | 久久久香蕉视频 | 成人午夜电影在线播放 | 精品国产伦一区二区三区观看说明 | 91网免费看 | 久久精品一区二区 | 久久开心激情 | 久久综合中文字幕 | 在线之家官网 | 久久久久国产精品一区 | 婷婷六月天综合 | 久久久综合九色合综国产精品 | 亚洲欧美日韩国产 | 欧美日韩亚洲第一 | 日韩在线在线 | 日韩免费一区二区三区 | 日韩电影在线一区二区 | 国内精品一区二区 | 成人黄色免费在线观看 | 米奇狠狠狠888 | 伊人va| 九九99视频 | 99久久久久久久久 | av免费在线看网站 | 久久一级电影 | 久久久精品福利视频 | 高清不卡一区二区在线 | 日韩午夜三级 | 狠狠色丁香婷综合久久 | 碰超人人 | 视频一区视频二区在线观看 | 超碰免费成人 | 欧美少妇影院 | 91av在线视频播放 | 欧美一区二区三区在线播放 | 日韩av一区二区三区 | 亚洲另类人人澡 | 欧美一区二区三区在线视频观看 | 啪啪凸凸 | 欧美三人交 | 999ZYZ玖玖资源站永久 | 美女免费视频网站 | 久久久男人的天堂 | 免费看色网站 | 成人97人人超碰人人99 | 99国产情侣在线播放 | 丁香婷婷激情网 | 在线免费观看黄色 | 91尤物国产尤物福利在线播放 | 特级毛片在线免费观看 | 91成人欧美| 亚洲免费在线播放视频 | 欧美在线久久 | 天天草天天干天天 | 久久成人福利 | 在线免费av观看 | 日韩免费在线网站 | 麻豆一精品传二传媒短视频 | 久99久精品视频免费观看 | 欧美日韩调教 | 国产99在线免费 | 超碰在97| 韩日在线一区 | 91视频xxxx| 国产伦理久久精品久久久久_ | 国产精品123| 亚欧洲精品视频在线观看 | 欧美日韩精品在线视频 | 中文字幕av影院 | 丁香 婷婷 激情 | 99视频这里只有 | 国产黄色片免费看 | 国产69精品久久久久久久久久 | 激情网色| 青青草国产精品视频 | 夜夜夜夜爽 | 伊人色综合网 | 国产伦理精品一区二区 | 在线成人欧美 | 激情久久五月 | 伊人久久国产 | 国产91精品在线观看 | 日韩免费在线观看视频 | 91片黄在线观看 | 亚洲精品国产精品久久99热 | 天天激情站 | 国产极品尤物在线 | 国产视频日韩视频欧美视频 | 国产免费观看av | 国产成人一区二区三区在线观看 | 久久久首页 | 黄色毛片网站在线观看 | 最近免费中文字幕 | 国产中文字幕av | 爱情影院aqdy鲁丝片二区 | 五月黄色 | 欧美成人tv | 国产精品精| 日韩免费一级电影 | 亚洲天堂va | 黄色片亚洲 | 欧美日韩在线观看一区二区 | 免费网站在线观看成人 | 国产高清在线免费 | 99r在线视频 | 一区二区高清在线 | 国产成人免费网站 | 国产视频2 | 一区二区三区电影在线播 | 国产91在线免费视频 | 国产精品久久影院 | 精品亚洲二区 | 国产91精品一区二区麻豆亚洲 | 人人玩人人添人人澡超碰 | 一区二区三区免费网站 | 九九热免费精品视频 | 91亚洲影院 | 国产高清网站 | 五月婷婷六月丁香激情 | 日韩视频免费播放 | 美女视频a美女大全免费下载蜜臀 | 中文字幕在线观看免费观看 | 国产精品高潮呻吟久久久久 | 97在线影院 | 国产亚洲精品久久久久久移动网络 | 欧美一级性生活 | 久久久国产日韩 | 丁香婷婷在线 | 热久久免费视频精品 | 久久久久久久久久久精 | 国产精品h在线观看 | 九九综合在线 | 久久激情视频免费观看 | 欧美成人精品欧美一级乱黄 | 亚洲aⅴ在线 | 最近最新中文字幕 | 久久久久久高潮国产精品视 | 国产又粗又猛又色又黄视频 | av在线播放不卡 | 久久久久久蜜桃一区二区 | 精品一区精品二区 | 久草在线手机视频 | 欧美成人精品欧美一级乱黄 | 99精品99| av女优中文字幕在线观看 | 天天操天天色天天射 | 国产精品久久免费看 | 91av在线国产| 五月视频 | 中文字幕乱在线伦视频中文字幕乱码在线 | 美女网站视频一区 | 欧美精品v国产精品 | 国产免码va在线观看免费 | 99亚洲视频| 国产亚州精品视频 | 国产精品久久久久一区 | 911久久| 91人人爽久久涩噜噜噜 | 日韩理论在线观看 | 97在线视频免费观看 | 亚洲好视频 | 91精品日韩| 国产精品自产拍在线观看网站 | 中文字幕免费观看 | 久久精品99精品国产香蕉 | 中文字幕色在线 | 国产福利小视频在线 | 国产又粗又猛又爽又黄的视频免费 | 色www精品视频在线观看 | 色欧美视频| av日韩精品 | 天天干天天操天天入 | 欧美激情综合色 | 美女福利视频 | 91精品国产三级a在线观看 | 日本黄色大片儿 | 中文字幕电影网 | 色综合久久久久综合体桃花网 | 国产小视频在线免费观看 | 久久九九久久 | 天天草天天 | 国产高清绿奴videos | 男女激情网址 | 日韩欧美视频二区 | 久久xxxx| 东方av在线免费观看 | 午夜av免费在线观看 | 色99在线 | 日韩激情视频 | 97色婷婷| 日日日爽爽爽 | 久草免费在线视频 | 婷婷六月在线 | 91超国产| 日韩丝袜视频 | 久久精品视频4 | 欧美精品三级在线观看 | 香蕉久草 | av在线免费观看网站 | 国产黄色av影视 | 最新国产中文字幕 | 91成人午夜 | 国产亚洲精品综合一区91 | 国产福利av在线 | 九九热精品视频在线观看 | 五月婷婷在线视频 | 欧美资源 | 国产区精品在线观看 | 久久少妇av | 久久免费电影网 | 日韩高清免费电影 | 毛片永久新网址首页 | 婷婷福利影院 | 欧美精品免费在线观看 | 黄色一级免费电影 | 99在线免费观看视频 | 天天摸夜夜添 | 色五丁香| 96亚洲精品久久久蜜桃 | 成人资源站 | 久久国产精品99久久人人澡 | 国产一卡二卡在线 | 久久美女电影 | 夜夜爽天天爽 | 午夜精品久久 | 国产免费xvideos视频入口 | 国产一级二级av | 久久久亚洲精华液 | 免费男女羞羞的视频网站中文字幕 | 成人中文字幕+乱码+中文字幕 | 69精品在线观看 | 福利视频第一页 | 狠狠狠干狠狠 | 97精品国产97久久久久久粉红 | 999久久| 日韩三级一区 | 亚洲一级二级 | 国产麻豆视频免费观看 | 亚洲精品国偷自产在线99热 | 国产成人精品久久久久蜜臀 | 国产夫妻性生活自拍 | 欧美精品一区在线 | 精品久久精品 | 一级片免费观看视频 | 国产日女人 | 亚洲高清视频一区二区三区 | 亚洲综合激情网 | 五月婷综合 | 黄色.com| 欧美精品久久久久久久免费 | 亚洲精品国偷拍自产在线观看蜜桃 | 久久免费一级片 | 91精品爽啪蜜夜国产在线播放 | 中文字幕在线观看一区二区 | 久久香蕉电影网 | 国产亚洲精品福利 | 九九九热精品免费视频观看 | 手机看片99| 国产一级电影免费观看 | 国产成人黄色在线 | 狠狠操狠狠干2017 | 天天操天天干天天干 | 日韩网| 欧美色图狠狠干 | 中文字幕在线观看免费高清电影 | 国产精品免费一区二区三区 | 中文字幕黄色网 | 国产色啪 | 欧美精品被 | 久久影院一区 | 国产精品日韩精品 | 2024国产精品视频 | 天海冀一区二区三区 | 欧美a影视 | 欧美日韩激情视频8区 | 蜜桃视频在线观看一区 | 国产精品资源在线观看 | 制服丝袜在线 | 中文字幕乱在线伦视频中文字幕乱码在线 | 日韩欧美综合精品 | 人人插人人 | www.天天操| 色999五月色 | 久久任你操 | 久热国产视频 | 亚洲国产成人在线播放 | 国产精品网红直播 | 欧美精品黑人性xxxx | 91av亚洲 | 成人黄大片视频在线观看 | 18岁免费看片 | 欧美日韩国产免费视频 | 91在线porny国产在线看 | 999视频在线播放 | 精品色综合 | 在线免费观看涩涩 | 成人av影院在线观看 | 国产一区播放 | 亚洲人成网站精品片在线观看 | 日韩电影中文字幕在线观看 | 香蕉成人在线视频 | 中文字幕在线播放日韩 | 欧美日韩国产高清视频 | 日韩av看片| 国产成人久久av免费高清密臂 | 中文字幕亚洲综合久久五月天色无吗'' | 伊人一级 | 欧美精品久久 | 97人人看| 欧美在线观看禁18 | 成人午夜电影免费在线观看 | 亚洲国产精品影院 | 午夜精品久久久久久久爽 | 99色精品视频 | 亚洲国产视频网站 | www黄色av | 久久精品电影 | 成人一区影院 | 伊人久久电影网 | 五月综合网| 西西www4444大胆在线 | 欧美在线观看视频一区二区三区 | 一区二区三区在线电影 | 国产精品久久久久av福利动漫 | 精品欧美一区二区精品久久 | 成人小视频在线观看免费 | 国产视频 亚洲精品 | 久久久久伦理电影 | 极品久久久 | 亚洲精品福利在线 | 免费看污污视频的网站 | 激情久久小说 | 999成人 | 婷婷久久婷婷 | 波多野结衣视频网址 | 午夜精品久久久久久久久久久久 | 91成品人影院 | 久久国产欧美日韩精品 | 国产麻豆果冻传媒在线观看 | 久草成人在线 | 久久精品视频18 | 亚洲一级片在线看 | 欧美一二在线 | 国内久久看 | 精品久久影院 | 在线观看亚洲成人 | 在线看片一区 | 最近中文字幕大全中文字幕免费 | 国产一级视屏 | 91九色蝌蚪国产 | 色偷偷网站视频 | 91av短视频 | 久久久.com | 国产91免费在线观看 | 手机看片中文字幕 | 91精品国产一区二区三区 | 国产精品黄色av | 国产精品亚洲人在线观看 | 色视频网址 | 亚洲精品高清视频在线观看 | 国产麻豆电影 | 一区二区三区免费在线观看 | 欧美成亚洲| 国产视 | 日韩中文字幕免费电影 | 一区在线电影 | 精品国产一区二区三区久久久蜜臀 | 欧美视频在线观看免费网址 | 久久福利电影 | 成人午夜电影免费在线观看 | 一区二区三区在线电影 | 国产黄免费 | 欧美精品999 | www.久久免费视频 | 麻豆94tv免费版 | 国产黄a三级三级三级三级三级 | 国产高清免费视频 | 激情婷婷色 | 少妇做爰k8经典 | 久久精品电影 | 国产婷婷视频在线 | 毛片美女网站 | 91av视屏| 美女在线黄 | 欧美日韩国产高清视频 | 成人中文字幕av | 九九在线国产视频 | 五月婷婷六月丁香激情 | 国产精品aⅴ | 久久看片网站 | 91精品久久久久久粉嫩 | 精品超碰 | 日韩视频在线观看视频 | 国产香蕉视频在线播放 | 日日精品 | 99精品视频在线观看视频 | 亚洲免费公开视频 | 国产精品一区二区三区在线免费观看 | 久久情爱 | 射综合网 | 亚洲精品xx| 国产在线观看h | 蜜臀av在线一区二区三区 | 久久只精品99品免费久23小说 | 伊人国产女 | 欧洲亚洲激情 | 美女久久久久久 | 欧美国产日韩一区二区三区 | 国产一区在线精品 | 精品久久久久久久久久久久久久久久久久 | 国产午夜在线观看视频 | 天天综合狠狠精品 | 丁香网婷婷 | 在线导航av| 成人影片在线播放 | 成人四虎影院 | 亚洲综合色视频在线观看 | 日本少妇视频 | 天堂网一区二区三区 | 日韩va欧美va亚洲va久久 | 911香蕉视频 | 一级黄色片网站 | 国产手机在线播放 | 国产精品一区在线观看 | 日韩精品视 | 国产成人精品亚洲 | 久久久久久久久精 | 久热只有精品 | 中文字幕高清有码 | 亚洲区色| 欧美一级视频免费看 | 久久精品视频免费播放 | 免费看麻豆 | 国产69精品久久久久9999apgf | www.91成人| 国产国语在线 | 国产97色在线 | 在线免费观看黄色小说 | 91av在线免费 | 激情网五月婷婷 | 欧美日韩精品在线免费观看 | 国产精品一区二区久久精品爱涩 | 黄色av一级片 | 五月天丁香亚洲 | 国产精品1区2区3区 久久免费视频7 | 亚洲精品美女在线观看播放 | 久久免费精品视频 | 午夜三级大片 | 探花视频免费观看高清视频 | 狠狠久久 | 一区三区在线欧 | 毛片一区二区 | 久久99精品久久久久婷婷 | 亚洲综合干 | 日韩在线视频播放 | 亚洲欧美经典 | 在线视频亚洲 | 国产一区二区三区视频在线 | 91一区二区在线 | 免费碰碰 | 久久久久久久国产精品 | 国产一级片一区二区三区 | 国产亚洲成人网 | 丝袜美腿在线播放 | 在线视频区 | 欧美激情奇米色 | 日韩动态视频 | 国产色黄网站 | 中文在线免费一区三区 | 久草在线视频看看 | 欧美日本不卡 | 日韩黄色免费在线观看 | 成人精品一区二区三区电影免费 | 精品久久一二三区 | 国产高清精 | 水蜜桃亚洲一二三四在线 | 男女日麻批 | 中文字幕在线观看你懂的 | 国产高潮久久 | 日日干夜夜操视频 | 免费a v视频| 日日天天av | 久久视频国产精品免费视频在线 | 色婷五月 | 国产精选在线观看 | 中文字幕国产精品 | 特级毛片在线观看 | 国产精品入口麻豆 | 国产大片黄色 | 日韩在线观看a | 夜夜婷婷 | 夜色资源站wwwcom | 天天在线操 | 96超碰在线| 婷婷中文字幕在线观看 | 91精品国产自产在线观看 | 免费在线播放视频 | 91在线免费视频观看 | 一区二区精品视频 | 香蕉在线播放 | 免费在线观看视频a | 在线你懂的视频 | 日本久久综合网 | 少妇高潮流白浆在线观看 | 久久久综合 | 日日日爽爽爽 | 免费看黄网站在线 | 国产精品成人一区二区三区 | 亚洲区视频在线观看 | 日韩欧美一区二区三区在线 | 亚洲精品在线电影 | 午夜久久久影院 | 91人人揉日日捏人人看 | 国产精品女 | 一区二区欧美日韩 | 国产精品午夜久久 | 亚洲开心激情 | 91精品久久久久久久久 | 亚洲欧美综合 | 国产激情小视频在线观看 | 日韩中文字| 日韩电影中文字幕在线 | 狠狠干激情 | 四虎在线免费观看视频 | 中文字幕日本在线观看 | www日韩精品 | 99免费在线播放99久久免费 | 天天操夜夜操国产精品 | 久久精品国产免费看久久精品 | 91精品视频在线观看免费 | 在线免费试看 | 国产精品久久久久久久久久了 | 国产人成看黄久久久久久久久 | 国产在线观看免费观看 | 久久人网 | 国产精品一区二区电影 | av免费网页| 成人h电影| 91精品久久久久久综合乱菊 | 亚洲国产99 | 久草国产精品 | 色999精品 | 天天草天天摸 | 国产日产欧美在线观看 | 在线观看久草 | 国产精品一区二区 91 | 日韩理论在线观看 | 在线观看国产 | 成人动漫视频在线 | 国产精品欧美精品 | 欧美一区二区三区在线视频观看 | 国产在线黄 | 精品国内自产拍在线观看视频 | 手机在线永久免费观看av片 | 99久久久久国产精品免费 | 国产一级在线免费观看 | 亚洲午夜久久久久久久久 | 97电影院网 | 日韩欧美视频二区 | 精品一区在线看 | 国内免费久久久久久久久久久 | 欧美孕妇与黑人孕交 | 美女视频久久 | 亚洲视频在线观看免费 | 久久久国产精品人人片99精片欧美一 | 久久综合色天天久久综合图片 | 国产在线久久久 | 色wwww| 狠狠干夜夜操天天爽 | 成人久久久久 | 久操中文字幕在线观看 | 高清有码中文字幕 | 91成人网在线观看 | 天天干天天玩天天操 | 日本中文一区二区 | 四虎亚洲精品 | 国产爽视频| 欧美黄色高清 | 欧美国产高清 | 日韩久久精品一区二区 | 五月天av在线| 国产一区免费 | 四虎国产精品成人免费4hu | 久久草草影视免费网 | 国产在线不卡 | 国产日韩在线视频 | 69性欧美| 日韩一级黄色片 | 成年人免费在线观看 | 久久a热6 | 蜜桃麻豆www久久囤产精品 | 91精品视频在线看 | 99在线免费视频观看 | 啪啪免费观看网站 | 免费av的网站 | 国产精品视频在线观看 | 中文字幕乱在线伦视频中文字幕乱码在线 | 国产精品久久久久久久久久不蜜月 | 亚洲国产成人高清精品 | 日韩成人邪恶影片 | 在线免费色 | 在线免费观看国产精品 | 欧美日韩免费看 | 成人国产精品免费观看 | 在线免费观看国产 | 亚洲成熟女人毛片在线 | 久久久www成人免费毛片 | 久久久在线 | 久久成人精品视频 | 久久99久久99精品免费看小说 | 久久免费视频一区 | 四虎影视久久久 | 天天曰天天爽 | 韩国av一区二区三区在线观看 | 午夜视频在线观看一区二区三区 | 手机看片99 | 国产黄色精品在线观看 | www久久国产| 精品国产伦一区二区三区观看体验 | 欧美日韩国产一区二区三区在线观看 | 久久高清国产视频 | 一级黄色在线免费观看 | 国产麻豆精品久久一二三 | 日韩精品免费在线观看视频 | 在线免费色视频 | 国产999久久久 | 最近免费中文字幕 | 狠狠色丁香婷婷综合视频 | 99久久电影 | 久久久 精品 | 日韩激情久久 | 亚洲第一香蕉视频 | 成人羞羞视频在线观看免费 | 美女网站在线观看 | 国产不卡一二三区 | 日韩精品视频免费看 | 黄色小说网站在线 | 在线观看中文 | 五月天综合激情 | 日韩午夜网站 | 久久激情视频 久久 | 亚洲国产视频在线 | 亚洲一区二区三区四区精品 | 五月天色丁香 | 成人黄色毛片视频 | 日韩高清免费在线 | 国产精品精 | 国产手机在线视频 | 亚洲成av人片一区二区梦乃 | 国产一级免费观看 | 96久久| 国产精品久久亚洲 | 久久艹在线观看 | 色在线国产 | 激情婷婷综合 | 婷婷伊人五月 | 91中文字幕网 | 久精品在线观看 | 九九九免费视频 | 91成人看片 | 激情综合亚洲精品 | 五月激情站 | 日韩网站在线播放 | 亚州中文av | 免费观看的黄色 | 91看片淫黄大片一级在线观看 | 日韩在线观| 国产亚洲精品bv在线观看 | 久久久久亚洲精品成人网小说 | 在线免费亚洲 | 成全在线视频免费观看 | 国产一区二区三区免费在线观看 | 99九九99九九九视频精品 | 亚洲女欲精品久久久久久久18 | 91 中文字幕 | 国产成人精品av在线观 | 精品国产一区二区三区久久久蜜臀 | 亚洲精品久久久久久中文传媒 | 国产精品麻豆果冻传媒在线播放 | 在线观看中文字幕av | 黄色免费电影网站 | 国产成视频在线观看 | 久草免费新视频 | 欧美日韩中 | 中文字幕乱偷在线 | 日韩专区在线观看 | 色先锋av资源中文字幕 | 五月婷婷综合在线观看 | 可以免费看av | 亚洲v精品 | 久久超碰在线 | 97超碰国产在线 | 丰满少妇高潮在线观看 | 日韩激情中文字幕 | 国产探花视频在线播放 | 麻豆视频在线免费看 | 日韩久久精品一区二区三区下载 | 在线观看中文字幕2021 | 美女视频久久黄 | 国产精品三级视频 | 亚洲精品国产成人 | 国产高清免费av | 国产精品成人国产乱一区 | 欧美黄在线 | 国产正在播放 | 免费瑟瑟网站 | 91系列在线观看 | 99久久久久久国产精品 | 日韩专区 在线 | 97精产国品一二三产区在线 | 欧美激情精品久久久久久变态 | 九九免费观看视频 | 久久久久这里只有精品 | av大全在线 | 久久精品一区八戒影视 | 操碰av | 国产无限资源在线观看 | 玖草影院 | 激情网婷婷 | 麻豆国产视频下载 | av成人动漫 | 色视频在线 | 国产97在线看 | 久久综合九色欧美综合狠狠 | 日日综合网 | 国产精品电影一区二区 | 中文字幕之中文字幕 | h视频在线看 | 久久久久久久久久久久av | 久久99免费观看 | 青青河边草观看完整版高清 | 中文视频在线 | 天天干天天操人体 | 国产精品久久久久久久久久直播 | 激情狠狠干 | 欧美日韩一区二区三区免费视频 | 欧美日韩国产在线观看 | 亚洲欧美国产日韩在线观看 | 91看片在线免费观看 | 97人人射 | 免费三及片 | 久久综合99 | 成年人免费在线观看网站 | 亚洲精品久久久蜜臀下载官网 | 五月婷婷伊人网 | 99精品视频在线观看 | 国产无吗一区二区三区在线欢 | 天堂网一区 | 色综合久久88色综合天天免费 | 亚洲精品欧洲精品 | 青青河边草免费视频 | 欧美最猛性xxxxx亚洲精品 | 一级黄色免费 | 黄色成人在线观看 | 黄网站色欧美视频 | 国产99中文字幕 | 久久久久久久久久久免费视频 | 免费观看第二部31集 | 午夜美女福利 | 丰满少妇一级片 | 色综合天天做天天爱 | 免费在线观看一区 | 久久久久久久久久久成人 | 蜜臀久久99静品久久久久久 | 色综合久久五月 | 久久99九九99精品 | 在线国产不卡 | 色吧av色av| 2019天天干天天色 | 97精品在线观看 | 国产一区免费观看 | 99国产精品免费网站 | 成年人免费看av | 中国一级片免费看 | 日韩成人精品 | www.婷婷色| 久久在线观看 | 男女激情片在线观看 | 国产精品永久免费在线 | 成人免费一级 | 免费欧美 | 日韩欧美视频一区 | 久久精品久久99 | 97超碰人人澡 | 精品久久视频 | 欧美日韩国产在线 | 黄色一级性片 | 亚洲精品在线观看av | 国产视频在线免费 | 91精品办公室少妇高潮对白 | 免费看在线看www777 | 18国产精品福利片久久婷 | av女优中文字幕在线观看 | av福利在线免费观看 | 四虎永久免费网站 | 国产小视频免费观看 | 日韩电影在线一区 | 国产免费亚洲高清 | 日韩在线播放av | 成人在线黄色电影 | 中文字幕韩在线第一页 | 奇人奇案qvod| 国产精品久久久视频 | 亚洲国产久 | 激情婷婷av | 99久久久国产免费 | 色婷婷综合五月 | 国际精品久久久久 | 综合国产在线 | 亚洲国产精品成人综合 | 天天伊人网 | av网站播放 | 久操操| 五月婷婷在线视频 | 91久久久久久久一区二区 | 精品一区电影国产 | 久久福利影视 | 免费看短 | 91粉色视频 | 91热视频 | 久艹视频在线免费观看 | 色多多视频在线 | 视频二区在线 | 亚洲精品五月 | 精品一区二区在线免费观看 | 在线观看911视频 | 人人玩人人添人人 | 日韩电影一区二区在线 | 狠狠狠色狠狠色综合 | 国产精品久久久久久久av电影 | 亚洲经典在线 | 视频一区亚洲 | 2020天天干天天操 | 久久九九影视 | 99久热在线精品 | 免费高清av在线看 | 午夜国产福利视频 | 国产精品va在线观看入 | 久久精品波多野结衣 | 91精品在线看| 黄网站免费久久 | 深爱五月激情网 | 99精品在线 | av网站免费看 | 国产视频一二三 | 91人人爽久久涩噜噜噜 | 久久久久国产精品视频 | 国产在线精品国自产拍影院 | 狠狠色婷婷丁香六月 | 在线观看免费av网 | 欧美黑人性爽 | 视频二区在线 | 婷婷丁香av | 国产精品美女久久久久久2018 | 欧美性做爰猛烈叫床潮 | 免费观看午夜视频 | 国产精品美女久久久 | 国产原厂视频在线观看 | 四虎国产精品永久在线国在线 | 久久不卡国产精品一区二区 | 在线观看黄污 | 国产视频二区三区 | 日韩美女久久 | 日韩字幕| 日韩精品在线视频免费观看 | 夜又临在线观看 | 麻豆你懂的| 成人a在线观看高清电影 | 久草视频免费 | 国产精品欧美 | 国产直播av| 午夜视频色 | av在线超碰| 最近中文字幕高清字幕在线视频 | 国产成人香蕉 | 香蕉网在线 | 最新av网址在线观看 | 精品久久一级片 | 久久久久久久久久久久影院 | 欧美成人在线免费观看 | 超碰日韩 | 成人黄视频 | 美州a亚洲一视本频v色道 | www黄在线| 高清免费在线视频 | 久草视频免费播放 | 成人黄色影片在线 | 久久国产精品99精国产 | 在线看国产一区 | 91在线视频免费观看 | 亚洲一区二区三区91 | 91在线免费观看国产 | 国产打女人屁股调教97 | 一区二区三区日韩在线 | 日韩免费高清在线 | 日本中文乱码卡一卡二新区 | 国产99久 | 日韩大陆欧美高清视频区 | 亚洲精品tv久久久久久久久久 | 视频一区二区在线 | 免费在线色 | 这里只有精品视频在线观看 | 天天爱天天舔 | www.夜夜骑.com| 中文字幕成人网 | 大片网站久久 | 国产一级片毛片 | 久草网在线观看 | 久久亚洲私人国产精品va | 婷婷在线资源 | 91在线影院 | 久草剧场 | 欧美巨乳网 | 日本xxxx裸体xxxx17 | 在线观看日韩视频 | 在线观看免费版高清版 | 国产视频在线观看一区二区 | 日韩精品一区电影 | 亚洲一区精品二人人爽久久 | 欧美日韩一区二区免费在线观看 | 99久久这里只有精品 | 香蕉视频啪啪 | 国产一级片不卡 | 色综合色综合久久综合频道88 | 九九导航| 久久精品久久久久久久 | 91一区二区在线 |