整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Spring Security 實戰干貨:必須掌握的

          Spring Security 實戰干貨:必須掌握的一些內置 Filter

          自公號:Felordcn

          1. 前言

          上一文我們使用 Spring Security 實現了各種登錄聚合的場面。其中我們是通過在 UsernamePasswordAuthenticationFilter 之前一個自定義的過濾器實現的。我怎么知道自定義過濾器要加在 UsernamePasswordAuthenticationFilter 之前。我在這個系列開篇說了 Spring Security 權限控制的一個核心關鍵就是 過濾器鏈 ,這些過濾器如下圖進行過濾傳遞,甚至比這個更復雜!這只是一個最小單元。

          Spring Security 內置了一些過濾器,他們各有各的本事。如果你掌握了這些過濾器,很多實際開發中的需求和問題都很容易解決。今天我們來見識一下這些內置的過濾器。

          2. 內置過濾器初始化

          Spring Security 初始化核心過濾器時 HttpSecurity 會通過將 Spring Security 內置的一些過濾器以 FilterComparator 提供的規則進行比較按照比較結果進行排序注冊。

          2.1 排序規則

          FilterComparator 維護了一個順序的注冊表 filterToOrder 。

           FilterComparator() {
           Step order=new Step(INITIAL_ORDER, ORDER_STEP);
           put(ChannelProcessingFilter.class, order.next());
           put(ConcurrentSessionFilter.class, order.next());
           put(WebAsyncManagerIntegrationFilter.class, order.next());
           put(SecurityContextPersistenceFilter.class, order.next());
           put(HeaderWriterFilter.class, order.next());
           put(CorsFilter.class, order.next());
           put(CsrfFilter.class, order.next());
           put(LogoutFilter.class, order.next());
           filterToOrder.put(
           "org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",
           order.next());
           filterToOrder.put(
           "org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter",
           order.next());
           put(X509AuthenticationFilter.class, order.next());
           put(AbstractPreAuthenticatedProcessingFilter.class, order.next());
           filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter",
           order.next());
           filterToOrder.put(
           "org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",
           order.next());
           filterToOrder.put(
           "org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter",
           order.next());
           put(UsernamePasswordAuthenticationFilter.class, order.next());
           put(ConcurrentSessionFilter.class, order.next());
           filterToOrder.put(
           "org.springframework.security.openid.OpenIDAuthenticationFilter", order.next());
           put(DefaultLoginPageGeneratingFilter.class, order.next());
           put(DefaultLogoutPageGeneratingFilter.class, order.next());
           put(ConcurrentSessionFilter.class, order.next());
           put(DigestAuthenticationFilter.class, order.next());
           filterToOrder.put(
           "org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter", order.next());
           put(BasicAuthenticationFilter.class, order.next());
           put(RequestCacheAwareFilter.class, order.next());
           put(SecurityContextHolderAwareRequestFilter.class, order.next());
           put(JaasApiIntegrationFilter.class, order.next());
           put(RememberMeAuthenticationFilter.class, order.next());
           put(AnonymousAuthenticationFilter.class, order.next());
           filterToOrder.put(
           "org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",
           order.next());
           put(SessionManagementFilter.class, order.next());
           put(ExceptionTranslationFilter.class, order.next());
           put(FilterSecurityInterceptor.class, order.next());
           put(SwitchUserFilter.class, order.next());
           }
          

          這些就是所有內置的過濾器。 他們是通過下面的方法獲取自己的序號:

           private Integer getOrder(Class<?> clazz) {
           while (clazz !=null) {
           Integer result=filterToOrder.get(clazz.getName());
           if (result !=null) {
           return result;
           }
           clazz=clazz.getSuperclass();
           }
           return null;
           }
          

          通過過濾器的類全限定名從注冊表 filterToOrder 中獲取自己的序號,如果沒有直接獲取到序號通過遞歸獲取父類在注冊表中的序號作為自己的序號,序號越小優先級越高。上面的過濾器并非全部會被初始化。有的需要額外引入一些功能包,有的看 HttpSecurity 的配置情況。 在上一篇https://www.felord.cn/spring-security-login.html中。我們禁用了 CSRF 功能,就意味著 CsrfFilter 不會被注冊。

          3. 內置過濾器講解

          接下來我們就對這些內置過濾器進行一個系統的認識。我們將按照默認順序進行講解

          3.1 ChannelProcessingFilter

          ChannelProcessingFilter 通常是用來過濾哪些請求必須用 https 協議, 哪些請求必須用 http 協議, 哪些請求隨便用哪個協議都行。它主要有兩個屬性:

          • ChannelDecisionManager 用來判斷請求是否符合既定的協議規則。它維護了一個 ChannelProcessor 列表 這些ChannelProcessor 是具體用來執行 ANY_CHANNEL 策略 (任何通道都可以), REQUIRES_SECURE_CHANNEL 策略 (只能通過https 通道), REQUIRES_INSECURE_CHANNEL 策略 (只能通過 http 通道)。
          • FilterInvocationSecurityMetadataSource 用來存儲 url 與 對應的ANY_CHANNEL、REQUIRES_SECURE_CHANNEL、REQUIRES_INSECURE_CHANNEL 的映射關系。

          ChannelProcessingFilter 通過 HttpScurity#requiresChannel() 等相關方法引入其配置對象 ChannelSecurityConfigurer 來進行配置。

          3.2 ConcurrentSessionFilter

          ConcurrentSessionFilter 主要用來判斷session是否過期以及更新最新的訪問時間。其流程為:

          1. session 檢測,如果不存在直接放行去執行下一個過濾器。存在則進行下一步。
          2. 根據sessionid從SessionRegistry中獲取SessionInformation,從SessionInformation中獲取session是否過期;沒有過期則更新SessionInformation中的訪問日期;
          3. 如果過期,則執行doLogout()方法,這個方法會將session無效,并將 SecurityContext 中的Authentication中的權限置空,同時在SecurityContenxtHoloder中清除SecurityContext然后查看是否有跳轉的 expiredUrl,如果有就跳轉,沒有就輸出提示信息。

          ConcurrentSessionFilter 通過SessionManagementConfigurer 來進行配置。

          3.3 WebAsyncManagerIntegrationFilter

          WebAsyncManagerIntegrationFilter用于集成SecurityContext到Spring異步執行機制中的WebAsyncManager。用來處理異步請求的安全上下文。具體邏輯為:

          1. 從請求屬性上獲取所綁定的WebAsyncManager,如果尚未綁定,先做綁定。
          2. 從asyncManager 中獲取 key 為 CALLABLE_INTERCEPTOR_KEY 的安全上下文多線程處理器 SecurityContextCallableProcessingInterceptor, 如果獲取到的為 null,
          3. 新建一個 SecurityContextCallableProcessingInterceptor 并綁定 CALLABLE_INTERCEPTOR_KEY 注冊到 asyncManager 中。

          這里簡單說一下 SecurityContextCallableProcessingInterceptor 。它實現了接口 CallableProcessingInterceptor,當它被應用于一次異步執行時,beforeConcurrentHandling() 方法會在調用者線程執行,該方法會相應地從當前線程獲取SecurityContext,然后被調用者線程中執行邏輯時,會使用這個 SecurityContext,從而實現安全上下文從調用者線程到被調用者線程的傳輸。

          WebAsyncManagerIntegrationFilter 通過 WebSecurityConfigurerAdapter#getHttp()方法添加到 HttpSecurity 中成為 DefaultSecurityFilterChain 的一個鏈節。

          3.4 SecurityContextPersistenceFilter

          SecurityContextPersistenceFilter 主要控制 SecurityContext 的在一次請求中的生命周期 。 請求來臨時,創建SecurityContext 安全上下文信息,請求結束時清空 SecurityContextHolder。

          SecurityContextPersistenceFilter 通過 HttpScurity#securityContext() 及相關方法引入其配置對象 SecurityContextConfigurer 來進行配置。

          3.5 HeaderWriterFilter

          HeaderWriterFilter 用來給 http 響應添加一些 Header,比如 X-Frame-Options, X-XSS-Protection ,X-Content-Type-Options。

          你可以通過 HttpScurity#headers() 來定制請求Header 。

          3.6 CorsFilter

          跨域相關的過濾器。這是Spring MVC Java配置和XML 命名空間 CORS 配置的替代方法, 僅對依賴于spring-web的應用程序有用(不適用于spring-webmvc)或 要求在javax.servlet.Filter 級別進行CORS檢查的安全約束鏈接。這個是目前官方的一些解讀,但是我還是不太清楚實際機制。

          你可以通過 HttpSecurity#cors() 來定制。

          3.7 CsrfFilter

          CsrfFilter 用于防止csrf攻擊,前后端使用json交互需要注意的一個問題。

          你可以通過 HttpSecurity.csrf() 來開啟或者關閉它。在你使用 jwt 等 token 技術時,是不需要這個的。

          3.8 LogoutFilter

          LogoutFilter 很明顯這是處理注銷的過濾器。

          你可以通過 HttpSecurity.logout() 來定制注銷邏輯,非常有用。

          3.9 OAuth2AuthorizationRequestRedirectFilter

          和上面的有所不同,這個需要依賴 spring-scurity-oauth2 相關的模塊。該過濾器是處理 OAuth2 請求首選重定向相關邏輯的。以后會我會帶你們認識它,請多多關注公眾號:Felordcn 。

          3.10 Saml2WebSsoAuthenticationRequestFilter

          這個需要用到 Spring Security SAML 模塊,這是一個基于 SMAL 的 SSO 單點登錄請求認證過濾器。

          關于SAML

          SAML 即安全斷言標記語言,英文全稱是 Security Assertion Markup Language。它是一個基于 XML 的標準,用于在不同的安全域(security domain)之間交換認證和授權數據。在 SAML 標準定義了身份提供者 (identity provider) 和服務提供者 (service provider),這兩者構成了前面所說的不同的安全域。 SAML 是 OASIS 組織安全服務技術委員會(Security Services Technical Committee) 的產品。

          SAML(Security Assertion Markup Language)是一個 XML 框架,也就是一組協議,可以用來傳輸安全聲明。比如,兩臺遠程機器之間要通訊,為了保證安全,我們可以采用加密等措施,也可以采用 SAML 來傳輸,傳輸的數據以 XML 形式,符合 SAML 規范,這樣我們就可以不要求兩臺機器采用什么樣的系統,只要求能理解 SAML 規范即可,顯然比傳統的方式更好。SAML 規范是一組 Schema 定義。

          可以這么說,在 Web Service 領域,schema 就是規范,在 Java 領域,API 就是規范

          3.11 X509AuthenticationFilter

          X509 認證過濾器。你可以通過 HttpSecurity#X509() 來啟用和配置相關功能。

          3.12 AbstractPreAuthenticatedProcessingFilter

          AbstractPreAuthenticatedProcessingFilter 處理處理經過預先認證的身份驗證請求的過濾器的基類,其中認證主體已經由外部系統進行了身份驗證。 目的只是從傳入請求中提取主體上的必要信息,而不是對它們進行身份驗證。

          你可以繼承該類進行具體實現并通過 HttpSecurity#addFilter 方法來添加個性化的AbstractPreAuthenticatedProcessingFilter 。

          3.13 CasAuthenticationFilter

          CAS 單點登錄認證過濾器 。依賴 Spring Security CAS 模塊

          3.14 OAuth2LoginAuthenticationFilter

          這個需要依賴 spring-scurity-oauth2 相關的模塊。OAuth2 登錄認證過濾器。處理通過 OAuth2 進行認證登錄的邏輯。

          3.15 Saml2WebSsoAuthenticationFilter

          這個需要用到 Spring Security SAML 模塊,這是一個基于 SMAL 的 SSO 單點登錄認證過濾器。 #關于SAML

          3.16 UsernamePasswordAuthenticationFilter

          這個看過我相關文章的應該不陌生了。處理用戶以及密碼認證的核心過濾器。認證請求提交的username和 password,被封裝成token進行一系列的認證,便是主要通過這個過濾器完成的,在表單認證的方法中,這是最最關鍵的過濾器。

          你可以通過 HttpSecurity#formLogin() 及相關方法引入其配置對象 FormLoginConfigurer 來進行配置。 我們在 https://www.felord.cn/spring-security-login.html 已經對其進行過個性化的配置和魔改。

          3.17 ConcurrentSessionFilter

          參見 #3.2 。 該過濾器可能會被多次執行。

          3.18 OpenIDAuthenticationFilter

          基于OpenID 認證協議的認證過濾器。 你需要在依賴中依賴額外的相關模塊才能啟用它。

          3.19 DefaultLoginPageGeneratingFilter

          生成默認的登錄頁。默認 /login 。

          3.20 DefaultLogoutPageGeneratingFilter

          生成默認的退出頁。 默認 /logout 。

          3.21 ConcurrentSessionFilter

          參見 #3.2 。 該過濾器可能會被多次執行。

          3.23 DigestAuthenticationFilter

          Digest身份驗證是 Web 應用程序中流行的可選的身份驗證機制 。DigestAuthenticationFilter 能夠處理 HTTP 頭中顯示的摘要式身份驗證憑據。你可以通過 HttpSecurity#addFilter() 來啟用和配置相關功能。

          3.24 BasicAuthenticationFilter

          和Digest身份驗證一樣都是Web 應用程序中流行的可選的身份驗證機制 。 BasicAuthenticationFilter 負責處理 HTTP 頭中顯示的基本身份驗證憑據。這個 Spring SecuritySpring Boot 自動配置默認是啟用的 。

          BasicAuthenticationFilter 通過 HttpSecurity#httpBasic() 及相關方法引入其配置對象 HttpBasicConfigurer 來進行配置。

          3.25 RequestCacheAwareFilter

          用于用戶認證成功后,重新恢復因為登錄被打斷的請求。當匿名訪問一個需要授權的資源時。會跳轉到認證處理邏輯,此時請求被緩存。在認證邏輯處理完畢后,從緩存中獲取最開始的資源請求進行再次請求。

          RequestCacheAwareFilter 通過 HttpScurity#requestCache() 及相關方法引入其配置對象 RequestCacheConfigurer 來進行配置。

          3.26 SecurityContextHolderAwareRequestFilter

          用來 實現j2ee中 Servlet Api 一些接口方法, 比如 getRemoteUser 方法、isUserInRole 方法,在使用 Spring Security 時其實就是通過這個過濾器來實現的。

          SecurityContextHolderAwareRequestFilter 通過 HttpSecurity.servletApi() 及相關方法引入其配置對象 ServletApiConfigurer 來進行配置。

          3.27 JaasApiIntegrationFilter

          適用于JAAS (Java 認證授權服務)。 如果 SecurityContextHolder 中擁有的 Authentication 是一個 JaasAuthenticationToken,那么該 JaasApiIntegrationFilter 將使用包含在 JaasAuthenticationToken 中的 Subject 繼續執行 FilterChain。

          3.28 RememberMeAuthenticationFilter

          處理 記住我 功能的過濾器。

          RememberMeAuthenticationFilter 通過 HttpSecurity.rememberMe() 及相關方法引入其配置對象 RememberMeConfigurer 來進行配置。

          3.29 AnonymousAuthenticationFilter

          匿名認證過濾器。對于 Spring Security 來說,所有對資源的訪問都是有 Authentication 的。對于無需登錄(UsernamePasswordAuthenticationFilter )直接可以訪問的資源,會授予其匿名用戶身份

          AnonymousAuthenticationFilter 通過 HttpSecurity.anonymous() 及相關方法引入其配置對象 AnonymousConfigurer 來進行配置。

          3.30 SessionManagementFilter

          Session 管理器過濾器,內部維護了一個 SessionAuthenticationStrategy 用于管理 Session 。

          SessionManagementFilter 通過 HttpScurity#sessionManagement() 及相關方法引入其配置對象 SessionManagementConfigurer 來進行配置。

          3.31 ExceptionTranslationFilter

          主要來傳輸異常事件,還記得之前我們見過的 DefaultAuthenticationEventPublisher 嗎?

          3.32 FilterSecurityInterceptor

          這個過濾器決定了訪問特定路徑應該具備的權限,訪問的用戶的角色,權限是什么?訪問的路徑需要什么樣的角色和權限?這些判斷和處理都是由該類進行的。如果你要實現動態權限控制就必須研究該類

          文通俗易懂地剖析用戶授權的設計原理和四種授權模式,重點介紹授權碼登錄模式,適合閱讀的人群:開放平臺/第三方合作的產品經理,初入職場的產品經理。

          1. 應用場景

          我們每個人都遇到過授權登錄的環節,授權登錄的應用無孔不入,可能你是在授權應用權限,或是授權賬戶登錄,或是授權個人信息。

          我們常見的應用場景一般有以下:

          • 新安裝應用:授權獲取存儲空間,設備信息等(手機原生彈窗)
          • 支付寶授權登錄淘寶:授權使用支付賬戶登錄淘寶APP(淘寶原生頁面)
          • 微信打開美團外賣:授權獲取你的頭像和地理位置(微信原生彈窗)

          因而授權登錄是應用間交互的重要且廣泛的步驟,深入了解過其中的原理很有必要。

          2. 授權登錄是什么?

          以美團外賣授權獲取你的頭像和地理位置為例:

          • 頭像和地理位置屬于你的個人信息,如需要傳輸,必須經得本人的同意,法律不允許默認傳輸。
          • 授權登錄是經得資源所有者(亦即是用戶)同意,服務提供商(亦即是微信,他為你提供授權服務)提供授權服務和應用方(他來使用你的授權)使用授權的過程。

          字面意思就是如下圖流程:

          值得關注的是第3步和第4步:當你在授權的過程中,實則是你和微信的直接交互,與美團外賣小程序無關。

          亦即是:你是跟微信同意授權,也是微信接收到你的“同意”的指令,即使在網站用微信登錄也是如此,如豆瓣登錄,需要微信掃描二維碼,確保授權動作保留和發生在微信自己的環境內。

          3. 授權登錄的模式

          那么從形式來說,授權登錄可以分為靜默授權和手動授權兩種模式:

          • 靜默授權:一般是用于獲取一些類似于用戶ID的信息,比如每個用戶在微信的ID被稱為openid,這種ID只是用戶的唯一身份認證(相當于編號),不包含個人信息,應用獲取openid并不能分析出你的手機號和身份證號這些個人信息。顯然,很多用戶都不知道openid是什么,總不能彈個彈窗問用戶“你是否同意傳輸openid”吧。因而這類傳輸,用戶是無感的,用戶只需訪問了某個頁面,后臺會向微信請求拿到你的openid。
          • 手動授權:這種亦即是我們上文提到的用戶場景,這類型場景需要獲取的信息是你的個人信息,比如頭像,昵稱,手機號和地址等等。這些個人信息是必須經過用戶手動點擊同意的。

          4. OAuth2原理及剖析

          以上第2點是授權的基本簡化,本節是更重點介紹OAuth2的系統鏈路流程(無論是靜默或是手動,系統鏈路一致,只是形式的區分)。目前市面上涉及授權,權限申請的業務均通過OAuth2的方法進行設計。

          OAuth2具體可以分為以下四種:

          1. 授權碼模式(authorization code)【重點】
          2. 簡化模式(implicit)
          3. 密碼模式(resource owner password credentials)
          4. 客戶端模式(client credentials)

          4.1 授權碼模式

          其中最重要的就是第一種授權碼模式,接下來我以企業微信授權碼方法做解析,其流程圖非常清晰。

          例子講解:

          場景:該身份授權是用戶在企業微信使用第三方應用時拉起授權頁面的流程。類似于你在微信打開餓了么小程序。

          系統交互的步驟:

          1. 用戶在企業微信打開一個A應用。此時A應用通過靜默推送獲取到用戶的userid,發現這個用戶沒有頭像和昵稱信息在A應用的數據庫。
          2. 此時,A應用調用企業微信的OAuth認證鏈接,這個鏈接要帶上企業ID(表明應用方),權限獲取范圍(頭像+昵稱),標記本次授權的編號(state)和授權完跳轉的地址,做好鏈接之后,向微信發送過去。
          3. 企業微信收到請求后,校驗企業ID和授權跳轉的地址是否對應。如果驗證通過,企業微信會給A應用一個令牌(code),并在前端打開企業微信的授權頁面(該頁面由企業微信管理)。
          4. 用戶點擊授權了之后,企業微信可以利用code和state向企業微信請求用戶信息API,獲得用戶token,最終獲得指定用戶信息。
          5. 同時用戶點擊授權后,企業微信關閉授權頁,并跳轉到A應用在第2步提供的跳轉地址。

          4.2 簡化模式

          請記住第一個模式中的第(1)步和第(2)步都需要A應用處理,簡化模式就是簡化了第(2)步。

          以下在微信的場景僅用于舉例:

          1. 用戶點擊應用入口之后,微信直接讓用戶是否同意授權(授權的內容和觸發時間提前配置好),用戶點擊同意。
          2. 用戶同意后,跳轉到該應用在后臺預留的地址,并且微信把訪問令牌直接告訴應用。
          3. 應用利用訪問令牌找微信獲取用戶信息,完成。

          此處你有沒有發現,前面授權的過程并不需要應用本身參與,這個就是比授權碼模式簡化的地方。但這種模式不支持用戶令牌的更新,也就是用戶第一次授權過期了之后,下一次又需要重新手動授權。

          4.3 密碼模式

          這種模式很直接,相當于你把你微信的賬戶密碼告訴餓了么,餓了么利用你的賬戶密碼去獲取信息。這種方式極其不安全,用戶的賬戶信息隨時會被外泄。

          4.4 客戶端模式

          這種其實不屬于授權,實則就是兩個應用間直接進行信息傳輸,與用戶無關。

          5. 總結

          1. 授權分為靜默授權和手動授權,一般出現在打開應用登錄的環節,應用廣泛。
          2. 授權的組件或頁面必須在擁有數據的應用中,這樣才能確保用戶在授權頁上同意協議,清晰看到傳輸的數據范圍,以及確保用戶親手同意授權。
          3. 授權分為四個模式,其中授權碼模式是應用最廣泛,最重要的模式。
          4. 授權碼模式亦即是A應用拼接企業參數向企業微信請求打開授權頁面,獲取用戶授權碼code,再利用code獲得用戶的token,最終獲取用戶信息。

          相關閱讀

          API接口入門(一):讀懂API接口文檔

          API接口入門(一):讀懂API接口文檔

          作者:就是愛睡覺,電商和金融業行業的產品,以TO B業務為主,文章是用于記錄自己在產品工作的思考和想法,希望有想法的小伙伴共同交流。

          本文由 @就是愛睡覺 原創發布于人人都是產品經理。未經許可,禁止轉載

          題圖來自Unsplash,基于CC0協議

          些項目或者系統的開發,避免不了有些非業務性代碼(也叫功能性代碼)。這類代碼一般是具有通用性特點,比如日志,權限,授權等等。作為高級工程師或者架構師,就要考慮把這些代碼進行封裝,方便別人使用。

          常用的封裝方法,一般分為三種 1:基類的方法抽象 2:工具類 3:自定義注解。今天我們說一下第三種自定義注解的方式。先簡單說一下spring注解的分類,主要兩種,一種是初始化類注解。例如 @Component,@Controller.....

          第二種就是AOP類注解,例如@Transcation,@Cache....。

          現在有個微信公眾號的授權使用場景,通過code獲取訪問用戶的openId,如果緩存存在,從緩存中獲取,不存在就從微信服務器獲取,之后得到用戶的相應信息,頭像,名稱等等。因為每個頁面都需要進行授權獲取用戶的openID,所以這個功能可以封裝起來,供所有模塊使用。

          第一步:定義注解,參數type用來區分是靜默授權還是非靜默授權。

          @Retention(RetentionPolicy.RUNTIME) // 注解會在class字節碼文件中存在,在運行時可以通過反射獲取到
          @Target({ElementType.FIELD, ElementType.METHOD})//定義注解的作用目標**作用范圍字段、枚舉的常量/方法
          @Documented //說明該注解將被包含在javadoc中
          public @interface AuthAnnotation {
          String type();
          }

          第二步:定義切面類AuthAspect

          @Aspect
          @Component
          public class AuthAspect {
          @Resource
          private ObjectCacheRedisDao objectCacheRedisDao;
           private BeLogger logger=new BeLogger(AuthAspect.class);
          @Around("@annotation(aa)")
          public Object doAround(ProceedingJoinPoint joinPoint,AuthAnnotation aa) throws Throwable {
          Object object=null;
          String type=aa.type();//后去注解的參數
          Object [] parm=joinPoint.getArgs();//獲得切面方法當中的參數
          String code=String.valueOf(parm[0]);
          JSONObject jsonObject=null;
          JSONObject userInfo=new JSONObject();
           if (StringUtils.isEmptyString(code)) {
          return userInfo;
          }
          String openId="";
          // 獲取授權信息
          if (!code.equals("authdeny") && objectCacheRedisDao.get(code)==null) {
          logger.debug("objectCache沒有數據。code:" + code);
           try {
          jsonObject=WechatClient.getUserTokenByAuthCode(code);
           if (jsonObject==null || !jsonObject.containsKey("openid")) {
          return userInfo;
          } else {
          openId=jsonObject.getString("openid");
          userInfo.put("openid",openId);
          objectCacheRedisDao.set(code, userInfo);
          }
          //非靜默授權的場合
          if("user_info".equals(type)) {
          String accessToken=jsonObject.getString("access_token");
          userInfo=WechatClient.getUserInfoByTokenAndOpenId(accessToken, openId);
           if (!userInfo.containsKey("errcode")) {
          objectCacheRedisDao.set(code, userInfo);
          }
          }
          } catch (Exception e) {
          logger.warn("YXKJ----------------->確認課次獲取用戶信息異常:" + Tools.getStackStr(e));
          }
          } else {
          logger.debug("objectCacheRedisDao有數據。code:" + code);
          userInfo=(JSONObject) objectCacheRedisDao.get(code);
          }
          if (userInfo !=null) {
          parm[1]=userInfo.toString();
          } else {
          parm[1]="";
          }
          object=joinPoint.proceed(parm);
           return object;
          }
          }

          第三步:自動為spring容器中那些配置@aspectJ切面的bean創建代理,織入切面

          <aop:aspectj-autoproxy proxy-target-class="true" />

          第四布:調用注解


          主站蜘蛛池模板: 日本免费精品一区二区三区| 日韩一区二区三区免费体验| 一区二区三区91| 无码8090精品久久一区| 午夜无码一区二区三区在线观看| 亚洲AⅤ视频一区二区三区| 精品人妻少妇一区二区| 久久99精品波多结衣一区| 精品人妻AV一区二区三区| 日韩一区二区三区不卡视频 | 亚洲图片一区二区| 亚洲日韩一区精品射精| 精品乱码一区二区三区在线| 怡红院一区二区三区| 日韩一区二区三区无码影院| 色综合视频一区二区三区| 国产福利一区二区在线视频| 亚洲视频一区网站| 精品一区二区三区自拍图片区| 久久国产精品亚洲一区二区| 久草新视频一区二区三区| 人妻内射一区二区在线视频| 狠狠做深爱婷婷综合一区| 久久国产午夜精品一区二区三区 | 日韩精品一区二区三区影院| 亚洲AV无码片一区二区三区| 视频一区二区在线观看| 久久国产香蕉一区精品| 国产精品高清一区二区三区不卡 | 国产一区二区三区高清视频| 亚洲一区二区三区乱码在线欧洲| 天堂不卡一区二区视频在线观看 | 精品国产一区二区三区在线观看 | 中文字幕AV无码一区二区三区| 精品一区二区三区在线观看l| 国产精品污WWW一区二区三区| 97精品一区二区视频在线观看 | 亚洲一区精品中文字幕| 色一情一乱一伦一区二区三区日本| 久久久久人妻精品一区蜜桃| 一区二区三区AV高清免费波多|