亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

為什么要緩存?

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

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

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

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

使用緩存

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

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接口聲明了緩存與恢復(fù)操作。默認(rèn)實(shí)現(xiàn)類是HttpSessionRequestCache。HttpSessionRequestCache的實(shí)現(xiàn)比較簡單,這里只列出接口的聲明:

public interface RequestCache {
  // 將request緩存到session中
  void saveRequest(HttpServletRequest request, HttpServletResponse response);
  // 從session中取request
  SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response);
  // 獲得與當(dāng)前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)下訪問受保護(hù)資源,AccessDeniedException指的是登陸了但是由于權(quán)限不足(比如普通用戶訪問管理員界面)。

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

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

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

對應(yīng)以下代碼

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 默認(rèn)實(shí)現(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);
      // 設(shè)置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 默認(rèn)實(shí)現(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步時,用戶未登錄的情況下訪問受保護(hù)資源,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)站認(rèn)證是信息存放在header中。第一次請求受保護(hù)資源時,請求頭中不包含認(rèn)證信息 ,驗(yàn)證失敗,該請求會被緩存,之后即使用戶填寫了信息,也會因?yàn)閞equest被恢復(fù)導(dǎo)致信息丟失從而認(rèn)證失敗(問題描述可以參見這里。

最簡單的方案當(dāng)然是不緩存request。

spring security 提供了NullRequestCache, 該類實(shí)現(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());

補(bǔ)充

默認(rèn)情況下,三種request不會被緩存。

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

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

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

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

相關(guān)文章

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

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

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

    關(guān)于java 圖形驗(yàn)證碼的解決方法

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

    詳解Java中接口的定義與實(shí)例代碼

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

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

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

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

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

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

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

    四步五分鐘Spring4快速集成Swagger

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

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

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

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

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

    Java 定時器的使用示例

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

最新評論