最近在做這個Tutorial, 基本是OAuth2典型的登錄流程:
1.用戶在自己網站的網頁上點擊“Login Using FaceBook”後跳轉到FaceBook(OAuth2 Auth Server)進行認證
2.到Auth Server的授權頁面進行授權
3.到自己網站的某URL觸發AccessToken的Exchange
4.獲取AccessToken成功後回到初始頁面。
因爲有自己的技術棧,所以並沒有採用官方的AngularJS做前端,我就簡單的用了Fetch+JQuery,結果出現以下問題:
OAuth2 Auth Server(我用的自己寫的server,不是FB)登錄成功後,瀏覽器並沒有跳轉回localhost/頁面,而是跳轉到了localhost/user的頁面,儘管登錄成功了,但是這個landing page是有問題的。
跟了一下源碼,記錄幾個知識點:
1.在OAuth2SsoProperties類中定義了DEFAULT_LOGIN_PATH,爲"/login",這個URL用於步驟3,是負責跟Auth server交換Token的URL,OAuth2流程中有個Return URI的概念,就是Auth Server在認證成功後要把你重定向到哪裏的URL。
2.在/login的業務邏輯做完之後,該把用戶重定向到哪裏呢? 當然應該重定向到用戶"上一次訪問"的頁面。這個"上一次訪問"的頁面是怎麼定義的?是在ExceptionTranslationFilter(這個Filter的功能請自己補充下知識)中觸發的,最終調用了HttpSessionRequestCache.saveRequest(),儲存了一個requestCache。也就是說,如果你訪問的頁面不是一個受到保護的頁面,Spring是不會保存這個request Cache的。 當然了,Spring也提供了其他重定向策略,比如可以一直重定向到一個固定頁面。
3. 從2可以看到,其實最終登錄成功要回到哪個頁面,已經在你點擊"login using XXX"這個按鈕時就已經決定了,應該是你在點這個按鈕之前訪問的最後一個受保護的頁面。那爲什麼官方文檔能回到"/主頁",我卻只能回到"/user"這個頁面呢?而且從流程來看,/user這個API是都要調用的,所以/user纔是本來應該的landing page,氣氛上我的結果反而是對的。。。
繼續跟到源碼看官方文檔的那個例子HttpSessionRequestCache.saveRequest()這個方法爲什麼沒把/user的訪問記錄給存下來,發現在做方法的第一行
requestMatcher.matches(request)
時候,代理到了MediaTypeRequestMatcher這個類,原因也沒往細看,主要的區別在於官方用Angular的話在request中添加了Accept的header,而我在用Fetch時候是沒這個Header的。
於是加上Accept:application/json,問題解決。
這種坑真是防不勝防。儘管現在Spring Boot讓一切都看起來簡單了,遇到坑的時候就能體會到這種便利是有代價的。。。