OAuth2.0實戰:認證、資源服務異常自定義!


大家好,我是不才陳某~

這是《Spring Security 進階》的第4篇文章,往期文章如下:

文章都是成體系的,陳某默認看到這篇文章的讀者都已經看了前期的文章,之前的知識點就不再詳細介紹了。

今天這篇文章主要介紹一下實際工作中使用Spring Security需要定制的一些異常信息。

文章目錄如下:

案例服務搭建

此篇文章沿用上篇文章的認證、資源服務,如下:

1、認證服務oauth2-auth-server-jwt

2、資源服務oauth2-auth-resource-jwt

案例源碼已經上傳GitHub,關注公號:碼猿技術專欄,回復關鍵詞:9529 獲取!

認證服務的異常

先來看一下正確的獲取令牌的請求,以密碼模式為例,如下圖:

密碼模式需要傳遞5個參數,分別是用戶名密碼客戶端id客戶端秘鑰授權類型

那么問題來了:如果任意一個參數傳錯了,返回什么?

1、用戶名、密碼錯誤

故意輸錯用戶名或者密碼,返回信息如下:

2、授權類型錯誤

輸入一個不存在的授權類型,返回信息如下:

3、客戶端ID,秘鑰錯誤

輸入錯誤的客戶端id或者秘鑰,返回信息如下:

感覺如何?是不是很酸爽?很顯然這返回的信息不適合前后端交互,別着急,下面介紹解決方案

認證服務自定義異常信息

上面列舉了三種常見的異常,解決方案實際可以分為兩種:

  • 用戶名,密碼錯誤異常、授權類型異常
  • 客戶端ID、秘鑰異常

陳某這里針對這兩種異常先上解決方案,后面再從源碼解釋為什么這么做?

1、用戶名,密碼錯誤異常、授權類型異常

針對用戶名、密碼、授權類型錯誤的異常解決方式比較復雜,需要定制的比較多。

1、定制提示信息、響應碼

這部分根據自己業務需要定制,陳某這里只是給出個例子,代碼如下:

2、自定義WebResponseExceptionTranslator

需要自定義一個異常翻譯器,默認的是DefaultWebResponseExceptionTranslator,此處必須重寫,其中有一個需要實現的方法,如下:

ResponseEntity<T> translate(Exception e) throws Exception;

這個方法就是根據傳遞過來的Exception判斷不同的異常返回特定的信息,這里需要判斷的異常的如下:

  • UnsupportedGrantTypeException:不支持的授權類型異常
  • InvalidGrantException:用戶名或者密碼錯誤的異常

創建一個OAuthServerWebResponseExceptionTranslator實現WebResponseExceptionTranslator,代碼如下:

3、認證服務配置文件中配置

需要將自定義的異常翻譯器OAuthServerWebResponseExceptionTranslator在配置文件中配置,很簡單,一行代碼的事。

AuthorizationServerConfig配置文件指定,代碼如下:

案例源碼已經上傳GitHub,關注公號:碼猿技術專欄,回復關鍵詞:9529 獲取!

4、測試

按照上述的配置完成后,測試下用戶名、密碼錯誤、授權類型錯誤是否能夠正確返回定制的提示信息,如下:

用戶名、密碼錯誤

授權類型不支持

5、源碼追蹤

實踐有了,總該理解一下為什么這么做吧?下面從源碼的角度告訴你為什么要這么做?

我們知道獲取令牌的接口為 /oauth/token,這個接口定義在TokenEndpoint#postAccessToken()(POST請求)方法中,如下圖:

我們先不看其中的邏輯,平時我們寫接口的異常怎么處理?

一般都是通過 @ExceptionHandler 特定的異常,然后統一的進行異常處理,是不是這樣?

然后看一下上述的兩種異常屬於什么類型的,如下:

UnsupportedGrantTypeException

InvalidGrantException

是不是都繼承了OAuth2Exception,那么嘗試在TokenEndpoint這個類中找找有沒有處理OAuth2Exception這個異常的處理器,果然找到了一個 handleException() 方法,如下:

可以看到,這里的異常翻譯器已經使用了我們自定義的OAuthServerWebResponseExceptionTranslator。可以看下默認的異常翻譯器是啥,代碼如下:

看到沒,就是這個DefaultWebResponseExceptionTranslator

問題又來了:為什么在配置文件中設置了OAuthServerWebResponseExceptionTranslator就會生效呢?

這個不得不看下 @EnableAuthorizationServer 這個注解了,源碼如下:

注入了這個AuthorizationServerEndpointsConfiguration配置類,其中注入了AuthorizationEndpoint這個bean,如下:

將自定義的異常翻譯器設置進入了AbstractEndpoint這個抽象類中,而TokenEndpoint正是繼承了這個抽象類,復用了其中的異常翻譯器,代碼如下:

哦了,問題解決了,學東西一定要知其所以然...............

2、客戶端ID、秘鑰異常

這部分比較復雜,想要理解還是需要些基礎的,解決這個異常的方案很多,陳某只是介紹其中一種,下面詳細介紹。

1、定制提示信息、響應碼

這部分根據自己業務需要定制,陳某這里只是給出個例子,代碼如下:

2、自定義AuthenticationEntryPoint

這個AuthenticationEntryPoint是不是很熟悉,前面的文章已經介紹過了,此處需要自定義來返回定制的提示信息。

創建OAuthServerAuthenticationEntryPoint,實現AuthenticationEntryPoint,重寫其中的方法,代碼如下:

3、改造ClientCredentialsTokenEndpointFilter

ClientCredentialsTokenEndpointFilter這個過濾器的主要作用就是校驗客戶端的ID、秘鑰,代碼如下:

有幾個重要的部分需要講一下,如下:

  • 構造方法中需要傳入第2步自定義的 OAuthServerAuthenticationEntryPoint
  • 重寫 getAuthenticationManager() 方法返回IOC中的AuthenticationManager
  • 重寫afterPropertiesSet() 方法,用於自定義認證失敗、成功處理器,失敗處理器中調用OAuthServerAuthenticationEntryPoint進行異常提示信息返回

4、OAuth配置文件中指定過濾器

只需要將自定義的過濾器添加到AuthorizationServerSecurityConfigurer中,代碼如下:

部分是添加過濾器,其中authenticationEntryPoint使用的是第2步自定義的OAuthServerAuthenticationEntryPoint

部分一定要注意:一定要去掉這行代碼,具體原因源碼解釋。

案例源碼已經上傳GitHub,關注公號:碼猿技術專欄,回復關鍵詞:9529 獲取!

5、測試

直接輸入錯誤的秘鑰,結果如下:

6、源碼追蹤

1、OAuthServerAuthenticationEntryPoint在何時調用?

OAuthServerAuthenticationEntryPoint這個過濾器繼承了 AbstractAuthenticationProcessingFilter 這個抽象類,一切的邏輯都在 doFilter() 中,陳某簡化了其中的關鍵代碼如下:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
    try {
        	//調用子類的attemptAuthentication方法,獲取參數並且認證
			authResult = attemptAuthentication(request, response);
		}
		catch (InternalAuthenticationServiceException failed) {
            //一旦認證異常,則調用unsuccessfulAuthentication方法,通過failureHandler處理
			unsuccessfulAuthentication(request, response, failed);
			return;
		}
		catch (AuthenticationException failed) {
            //一旦認證異常,則調用unsuccessfulAuthentication方法,通過failureHandler處理
			unsuccessfulAuthentication(request, response, failed);
			return;
		}
		//認證成功,則調用successHandler處理
		successfulAuthentication(request, response, chain, authResult);
}

關鍵代碼在 unsuccessfulAuthentication() 這個方法中,代碼如下:

2、自定義的過濾器如何生效的?

這個就要看 AuthorizationServerSecurityConfigurer#configure() 這個方法了,其中有一段代碼如下:

這段代碼就是遍歷添加的過濾器將其添加到過濾器鏈中,在BasicAuthenticationFilter這個過濾器之前。

添加到security的過濾器鏈中,這個過濾器自然會生效了。

3、為什么不能加.allowFormAuthenticationForClients()?

還是在 AuthorizationServerSecurityConfigurer#configure() 這個方法中,一旦設置了 allowFormAuthenticationForClients 為true,則會創建 ClientCredentialsTokenEndpointFilter,此時自定義的自然失效了。

資源服務器的異常

從認證服務獲取到令牌之后去請求資源服務的資源,這里涉及到的異常主要有兩個,如下:

1、令牌失效

比如令牌不正確、過期,此時返回的異常提示如下:

2、權限不足

令牌的權限不足,比如 /admin 接口只允許 admin 角色訪問,此時返回的異常信息如下:

資源服務自定義異常信息

下面針對上述兩種異常分別定制異常提示信息,這個比認證服務定制簡單。

1、令牌失效

這個比較簡單,也是需要自定義AuthenticationEntryPoint。步驟如下:

1、自定義AuthenticationEntryPoint

這個和認證服務的客戶端異常類似,這里不再詳細說了,直接貼代碼,如下:

2、OAuth配置文件中配置

這個比較簡單,直接在配置文件中配置即可,代碼如下:

3、測試

此時拿着失效的令牌訪問資源服務,可以看到已經正常返回定制的提示信息了,如下:

源碼和認證服務的類似,自己斷點試試,還是很簡單的。

案例源碼已經上傳GitHub,關注公號:碼猿技術專欄,回復關鍵詞:9529 獲取!

2、權限不足

這個異常定制就更簡單了,陳某在第一篇文章:實戰!Spring Boot Security+JWT前后端分離架構登錄認證!介紹過,下面簡單的貼下代碼。

1、自定義AccessDeniedHandler

代碼如下:

2、OAuth配置文件中配置

和令牌失效的異常配置在同一個方法中,代碼如下:

3、測試

訪問 /admin 接口,此時的提示信息如下:

案例源碼已經上傳GitHub,關注公號:碼猿技術專欄,回復關鍵詞:9529 獲取!

最后說一句(別白嫖,求關注)

陳某每一篇文章都是精心輸出,已經寫了3個專欄,整理成PDF,獲取方式如下:

  1. 《Spring Cloud 進階》PDF:關注公號:【碼猿技術專欄】回復關鍵詞 Spring Cloud 進階 獲取!
  2. 《Spring Boot 進階》PDF:關注公號:【碼猿技術專欄】回復關鍵詞 Spring Boot進階 獲取!
  3. 《Mybatis 進階》PDF:關注公號:【碼猿技術專欄】回復關鍵詞 Mybatis 進階 獲取!

如果這篇文章對你有所幫助,或者有所啟發的話,幫忙點贊在看轉發收藏,你的支持就是我堅持下去的最大動力!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM