欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入淺析 Spring Security 緩存請求問題

 更新時間:2019年04月23日 09:51:36   投稿:mrr  
這篇文章主要介紹了 Spring Security 緩存請求問題,本文通過實例文字相結合的形式給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友參考下吧

為什么要緩存?

為了更好的描述問題,我們拿使用表單認證的網(wǎng)站舉例,簡化后的認證過程分為7步:

  1. 用戶訪問網(wǎng)站,打開了一個鏈接(origin url)。
  2. 請求發(fā)送給服務器,服務器判斷用戶請求了受保護的資源。
  3. 由于用戶沒有登錄,服務器重定向到登錄頁面
  4. 填寫表單,點擊登錄
  5. 瀏覽器將用戶名密碼以表單形式發(fā)送給服務器
  6. 服務器驗證用戶名密碼。成功,進入到下一步。否則要求用戶重新認證(第三步)
  7. 服務器對用戶擁有的權限(角色)判定: 有權限,重定向到origin url; 權限不足,返回狀態(tài)碼403("forbidden").

從第3步,我們可以知道,用戶的請求被中斷了。

用戶登錄成功后(第7步),會被重定向到origin url,spring security通過使用緩存的request,使得被中斷的請求能夠繼續(xù)執(zhí)行。

使用緩存

用戶登錄成功后,頁面重定向到origin url。瀏覽器發(fā)出的請求優(yōu)先被攔截器RequestCacheAwareFilter攔截,RequestCacheAwareFilter通過其持有的RequestCache對象實現(xiàn)request的恢復。

public void doFilter(ServletRequest request, ServletResponse response,
      FilterChain chain) throws IOException, ServletException {

    // request匹配,則取出,該操作同時會將緩存的request從session中刪除
    HttpServletRequest wrappedSavedRequest = requestCache.getMatchingRequest(
        (HttpServletRequest) request, (HttpServletResponse) response);

    // 優(yōu)先使用緩存的request
    chain.doFilter(wrappedSavedRequest == null ? request : wrappedSavedRequest,
        response);
  }

何時緩存

首先,我們需要了解下RequestCache以及ExceptionTranslationFilter。

RequestCache

RequestCache接口聲明了緩存與恢復操作。默認實現(xiàn)類是HttpSessionRequestCache。HttpSessionRequestCache的實現(xiàn)比較簡單,這里只列出接口的聲明:

public interface RequestCache {
  // 將request緩存到session中
  void saveRequest(HttpServletRequest request, HttpServletResponse response);
  // 從session中取request
  SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response);
  // 獲得與當前request匹配的緩存,并將匹配的request從session中刪除
  HttpServletRequest getMatchingRequest(HttpServletRequest request,
      HttpServletResponse response);
  // 刪除緩存的request
  void removeRequest(HttpServletRequest request, HttpServletResponse response);
}

ExceptionTranslationFilter

ExceptionTranslationFilter 是Spring Security的核心filter之一,用來處理AuthenticationException和AccessDeniedException兩種異常。

在我們的例子中,AuthenticationException指的是未登錄狀態(tài)下訪問受保護資源,AccessDeniedException指的是登陸了但是由于權限不足(比如普通用戶訪問管理員界面)。

ExceptionTranslationFilter 持有兩個處理類,分別是AuthenticationEntryPoint和AccessDeniedHandler。

ExceptionTranslationFilter 對異常的處理是通過這兩個處理類實現(xiàn)的,處理規(guī)則很簡單:

  1. 規(guī)則1. 如果異常是 AuthenticationException,使用 AuthenticationEntryPoint 處理
  2. 規(guī)則2. 如果異常是 AccessDeniedException 且用戶是匿名用戶,使用 AuthenticationEntryPoint 處理
  3. 規(guī)則3. 如果異常是 AccessDeniedException 且用戶不是匿名用戶,如果否則交給 AccessDeniedHandler 處理。

對應以下代碼

private void handleSpringSecurityException(HttpServletRequest request,
      HttpServletResponse response, FilterChain chain, RuntimeException exception)
      throws IOException, ServletException {
    if (exception instanceof AuthenticationException) {
      logger.debug(
          "Authentication exception occurred; redirecting to authentication entry point",
          exception);
      sendStartAuthentication(request, response, chain,
          (AuthenticationException) exception);
    }
    else if (exception instanceof AccessDeniedException) {
      if (authenticationTrustResolver.isAnonymous(SecurityContextHolder
          .getContext().getAuthentication())) {
        logger.debug(
            "Access is denied (user is anonymous); redirecting to authentication entry point",
            exception);
        sendStartAuthentication(
            request,
            response,
            chain,
            new InsufficientAuthenticationException(
                "Full authentication is required to access this resource"));
      }
      else {
        logger.debug(
            "Access is denied (user is not anonymous); delegating to AccessDeniedHandler",
            exception);
        accessDeniedHandler.handle(request, response,
            (AccessDeniedException) exception);
      }
    }
  }

AccessDeniedHandler 默認實現(xiàn)是 AccessDeniedHandlerImpl。該類對異常的處理是返回403錯誤碼。

public void handle(HttpServletRequest request, HttpServletResponse response,
      AccessDeniedException accessDeniedException) throws IOException,
      ServletException {
  if (!response.isCommitted()) {
    if (errorPage != null) { // 定義了errorPage
      // errorPage中可以操作該異常
      request.setAttribute(WebAttributes.ACCESS_DENIED_403,
          accessDeniedException);
      // 設置403狀態(tài)碼
      response.setStatus(HttpServletResponse.SC_FORBIDDEN);
      // 轉(zhuǎn)發(fā)到errorPage
      RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);
      dispatcher.forward(request, response);
    }
    else { // 沒有定義errorPage,則返回403狀態(tài)碼(Forbidden),以及錯誤信息
      response.sendError(HttpServletResponse.SC_FORBIDDEN,
          accessDeniedException.getMessage());
    }
  }
}

AuthenticationEntryPoint 默認實現(xiàn)是 LoginUrlAuthenticationEntryPoint, 該類的處理是轉(zhuǎn)發(fā)或重定向到登錄頁面

public void commence(HttpServletRequest request, HttpServletResponse response,
      AuthenticationException authException) throws IOException, ServletException {
  String redirectUrl = null;
  if (useForward) {
    if (forceHttps && "http".equals(request.getScheme())) {
      // First redirect the current request to HTTPS.
      // When that request is received, the forward to the login page will be
      // used.
      redirectUrl = buildHttpsRedirectUrlForRequest(request);
    }
    if (redirectUrl == null) {
      String loginForm = determineUrlToUseForThisRequest(request, response,
          authException);
      if (logger.isDebugEnabled()) {
        logger.debug("Server side forward to: " + loginForm);
      }
      RequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);
      // 轉(zhuǎn)發(fā)
      dispatcher.forward(request, response);
      return;
    }
  }
  else {
    // redirect to login page. Use https if forceHttps true
    redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);
  }
  // 重定向
  redirectStrategy.sendRedirect(request, response, redirectUrl);
}

了解完這些,回到我們的例子。

第3步時,用戶未登錄的情況下訪問受保護資源,ExceptionTranslationFilter會捕獲到AuthenticationException異常(規(guī)則1)。頁面需要跳轉(zhuǎn),ExceptionTranslationFilter在跳轉(zhuǎn)前使用requestCache緩存request。

protected void sendStartAuthentication(HttpServletRequest request,
      HttpServletResponse response, FilterChain chain,
      AuthenticationException reason) throws ServletException, IOException {
  // SEC-112: Clear the SecurityContextHolder's Authentication, as the
  // existing Authentication is no longer considered valid
  SecurityContextHolder.getContext().setAuthentication(null);
  // 緩存 request
  requestCache.saveRequest(request, response);
  logger.debug("Calling Authentication entry point.");
  authenticationEntryPoint.commence(request, response, reason);
}

一些坑

在開發(fā)過程中,如果不理解Spring Security如何緩存request,可能會踩一些坑。

舉個簡單例子,如果網(wǎng)站認證是信息存放在header中。第一次請求受保護資源時,請求頭中不包含認證信息 ,驗證失敗,該請求會被緩存,之后即使用戶填寫了信息,也會因為request被恢復導致信息丟失從而認證失敗(問題描述可以參見這里。

最簡單的方案當然是不緩存request。

spring security 提供了NullRequestCache, 該類實現(xiàn)了 RequestCache 接口,但是沒有任何操作。

public class NullRequestCache implements RequestCache {
  public SavedRequest getRequest(HttpServletRequest request,
      HttpServletResponse response) {
    return null;
  }
  public void removeRequest(HttpServletRequest request, HttpServletResponse response) {
  }
  public void saveRequest(HttpServletRequest request, HttpServletResponse response) {
  }
  public HttpServletRequest getMatchingRequest(HttpServletRequest request,
      HttpServletResponse response) {
    return null;
  }
}

配置requestCache,使用如下代碼即可:

http.requestCache().requestCache(new NullRequestCache());

補充

默認情況下,三種request不會被緩存。

  1. 請求地址以/favicon.ico結尾
  2. header中的content-type值為application/json
  3. header中的X-Requested-With值為XMLHttpRequest

可以參見:RequestCacheConfigurer類中的私有方法createDefaultSavedRequestMatcher。

附上實例代碼: https://coding.net/u/tanhe123/p/SpringSecurityRequestCache

以上所述是小編給大家介紹的Spring Security 緩存請求問題,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
如果你覺得本文對你有幫助,歡迎轉(zhuǎn)載,煩請注明出處,謝謝!

相關文章

  • Java中的5種同步輔助類介紹

    Java中的5種同步輔助類介紹

    你提交了一些任務,但你想等它們都完成了再做另外一些事情;你提交了一些任務,但是不想讓它們立刻執(zhí)行,等你喊123開始的時候,它們才開始執(zhí)行;等等這些場景,線程之間需要相互配合,或者等待某一個條件成熟執(zhí)行。這些場景想你就需要用到同步輔助類
    2014-04-04
  • 關于java 圖形驗證碼的解決方法

    關于java 圖形驗證碼的解決方法

    本篇文章小編為大家介紹,在java中,使用圖形驗證碼的解決方法。需要的朋友參考下
    2013-04-04
  • 詳解Java中接口的定義與實例代碼

    詳解Java中接口的定義與實例代碼

    這篇文章主要介紹了詳解Java中接口的定義與實例代碼的相關資料,需要的朋友可以參考下
    2017-03-03
  • springboot讀取yml文件中的list列表、數(shù)組、map集合和對象方法實例

    springboot讀取yml文件中的list列表、數(shù)組、map集合和對象方法實例

    在平時的yml配置文件中,我們經(jīng)常使用到配置基本數(shù)據(jù)類型的字符串,下面這篇文章主要給大家介紹了關于springboot讀取yml文件中的list列表、數(shù)組、map集合和對象的相關資料,需要的朋友可以參考下
    2023-02-02
  • spring聲明式事務@Transactional開發(fā)常犯的幾個錯誤及最新解決方案

    spring聲明式事務@Transactional開發(fā)常犯的幾個錯誤及最新解決方案

    使用聲明式事務@Transactional進行事務一致性的管理,在開發(fā)過程中,發(fā)現(xiàn)很多開發(fā)同學都用錯了spring聲明式事務@Transactional或使用不規(guī)范,導致出現(xiàn)各種事務問題,這篇文章主要介紹了spring聲明式事務@Transactional開發(fā)常犯的幾個錯誤及解決辦法,需要的朋友可以參考下
    2024-02-02
  • java版微信和支付寶退款接口

    java版微信和支付寶退款接口

    這篇文章主要為大家詳細介紹了java版微信退款接口和java版支付寶退款接口,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • 四步五分鐘Spring4快速集成Swagger

    四步五分鐘Spring4快速集成Swagger

    這篇文章主要為大家詳細介紹了四步、五分鐘Spring4快速集成Swagger的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • 阿里云主機上安裝jdk 某庫出現(xiàn)問題的解決方法

    阿里云主機上安裝jdk 某庫出現(xiàn)問題的解決方法

    今天安裝jdk到阿里云服務上,首先看下阿里云是32位還是64位的,如果是32位下載32位的包,如果是64位的下載64位的包,下面與大家分享下安裝過程中遇到問題的解決方法
    2013-06-06
  • Java System類用法實戰(zhàn)案例

    Java System類用法實戰(zhàn)案例

    這篇文章主要介紹了Java System類用法,結合具體實例形式分析了java使用System類獲取系統(tǒng)環(huán)境變量信息相關操作技巧,需要的朋友可以參考下
    2019-07-07
  • Java 定時器的使用示例

    Java 定時器的使用示例

    這篇文章主要介紹了Java 定時器的使用,幫助大家更好的理解和使用Java time類,感興趣的朋友可以了解下
    2020-09-09

最新評論