解決:spring security 登錄頁停留時間過長 跳轉至 403頁面

前言:最近的項目中用到了spring security組件,說句顯low的話:我剛開始都不知道用了security好不勒,提了bug,在改的過程中,遇到了一些問題,找同事交流,才知道是用的security組件。  這個bug,真的是一波三折:復現它就是個問題,然後我又把403改成了404,後來乾脆登錄不進去主站,最後,這個bug,被消滅在本寶寶的代碼中,哈哈哈哈哈!


問題所在:token 過期

一、關於問題的想法

1,我在想是不是寫的登錄邏輯有問題,用代碼控制住了當前登錄頁的相關緩存問題,一路跟代碼。 好吧,有些工程沒權限就不說了,後來同事告訴我應該沒又跳轉到邏輯,就死掉了。 我在登錄頁發現了那個 security的標記(當時根本不知道那是個啥,傻X一個),查了查,再跟同事瞭解了具體情況,果斷放棄對邏輯的懷疑,因爲我們沒有用代碼控制緩存失效之類的

2,既然不是用戶代碼的問題,那就是組件機制的問題唄。 本寶寶立馬翻了spring security的文檔,裏面真的提到了超時的概念。 並提出了集中解決方案,鏈接地址如下:https://docs.spring.io/spring-security/site/docs/4.1.3.RELEASE/reference/htmlsingle/#csrf-timeouts  好吧,裏面提到了通過使用JS函數,在表單提交前獲取到一個token值(通過使用spring提供的CsrfTokenArgumentResolver) 然而本寶寶並沒有在這時候幹掉這個不能算是bug的被提出的bug.也提到自定義處理相關異常的建議。 不過本寶寶一向都不是安分守己的人,好不容易整出一個bug,不折騰會兒,會遭天譴的,以下就是本寶寶驗證演算的方案示例:

a:本來就是spring security的一種安全保護機制,不算bug,去跟測試和產品協商,忽略它——哈哈,當然這是下下策,雖然最爲省事兒,但我預估99%會死

b:既然是停留時間過長才產生的問題,那我想辦法,讓用戶不管在什麼時候點擊登錄,就跟他剛剛輸入完賬戶信息一樣。 所以我就想到了:<METAHTTP-EQUIV="REFRESH"CONTENT="csrf_timeout_in_seconds"> 然而,在我的案例裏面,並沒有用啊,憂傷。

c:使用token註冊機,弄出一個不過時的,給登錄頁面使用——哈哈,我想的挺好的。 再不行了,我找到關於這個token過期時間的代碼,改掉它——果然一副寫代碼到死的精神

d:惹急了我,我把這個csrf的token驗證功能關掉——簡單又粗暴吧,嘿嘿——當然,這是決定不能幹的事兒,雖然可以解決我那個bug

e:總之是出了異常,系統纔會攔截到,然後跳轉到了指定的403頁面,那我就在它跳轉到403頁面之前,截住這個異常,然後由我自定義處理

f:這一條其實和第 d 條類似,我既然不能關掉所有的驗證,那我關掉登錄頁的驗證總行吧,登錄頁校驗的本來就不是這個頁面,而是用戶的權限。誰都可以進入到登錄頁,不是嗎,嘿嘿。 然後,本寶寶改寫了攔截器,對登錄頁的請求放行了。 ——然而,可能是人品有問題,我竟然沒法兒正常登錄了,憂傷。肯定是哪兒被我寫錯了。


其實我想法可多了,總有一個能幹掉這個bug,在幾次憂傷之後,我選了自定義異常攔截這種方案,這條線的思路是:斷點,看看它在跳轉到403之前,到底攔截到了什麼異常;找到異常,我自定義攔截,並做出我想要系統做出的反應

二、關鍵代碼

自定義異常處理:

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.csrf.MissingCsrfTokenException;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @author 何紅霞~Angelina
 */
public class MissingCsrfTokenAccessDeniedHandler extends AccessDeniedHandlerImpl {
    private RequestCache requestCache = new HttpSessionRequestCache();
    private String loginPage = "/login";

    @Override
    public void handle(HttpServletRequest req, HttpServletResponse res, AccessDeniedException exception) throws IOException, ServletException {
        if (exception instanceof MissingCsrfTokenException && isSessionInvalid(req)) {
            requestCache.saveRequest(req, res);
            req.getRequestDispatcher(loginPage).forward(req, res);

        }
        super.handle(req, res, exception);
    }

    private boolean isSessionInvalid(HttpServletRequest req) {
        try {
            HttpSession session = req.getSession(false);
            return session == null || !req.isRequestedSessionIdValid();
        }
        catch (IllegalStateException ex) {
            return true;
        }
    }

}

好吧,其實這裏又鬧了一個笑話。 我最開始,把轉發搞成重定向了,我靠,之前是token丟失,現在是整個參數都沒了,想掐死自己的心都有了。 果真是久了不寫基礎代碼,手生啊!

安全配置:

    @Bean
    public AccessDeniedHandler getAccessDeniedHandler() {
        return new MissingCsrfTokenAccessDeniedHandler();
    }
http.exceptionHandling().accessDeniedHandler(getAccessDeniedHandler());

三、總結

按照上面的方法做,問題解決了。 反正目前我自測是沒毛病了,有問題再說吧,哈哈哈哈哈。

其實這之中發生了幾個插曲,我覺得可以分享一下:

1,最開始是跳轉到403頁面,結果我一通改,403是不跳了,但跳404了。 這時候,你怎麼想問題?  反正我想的是:我既然能把403改成跳到404,我就一定能讓它跳轉到我想要讓它去的地方,我離成功只差最後一點了。  有時候,最恐怖的不是bug被改變了,而是不管你怎麼改,那個bug都沒有變過,你知道這意味着什麼嗎?

2,我有時候覺得,我是心裏變態的,因爲我特別希望系統整出個大bug,最好是那種,無從下手的bug。 這想法不好,我得改!

3,我覺得,改bug是一件很有成就感和幸福感的事兒,我以前是苦逼的挖坑,現在是幸福的挖坑,然後幸福的填坑。 我改bug可開心了,果然天生的勞碌命,一輩子的碼農啊。。。。。。    


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章