1. 程式人生 > >Spring Security OAuth2 授權失敗(401 問題整理

Spring Security OAuth2 授權失敗(401 問題整理

控制 默認 pub available cli npr href sta -s

Spring Cloud架構中采用Spring Security OAuth2作為權限控制,關於OAuth2詳細介紹可以參考 http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html

項目中采用OAuth2四種模式中的兩種,Password模式和Client模式, Password模式用於控制用戶的登錄,Client模式用於控制後端服務相互調用。

權限架構調整後在近期發現一些問題,由於網上資料不多,只能單步調試方式看源碼 (其實帶著問題看源碼是最好的方式)。現在將問題和解決方案整理如下:

一、免登錄接口校驗token問題

問題:APP端反饋,一些免登錄接口會校驗token

詳細:經過測試發現,免登錄接口 如果傳了access_token會對token合法性就行校驗,如果不傳接口不會校驗,這導致了免登錄接口的過期token會報錯

排查:經過查看源碼發現spring-security-oauth2的過濾器 OAuth2AuthenticationProcessingFilter 會對請求進行攔截,(具體源碼就不截圖了)

    • 如果存在access_token 則會根據userInfoEndpointUrl去認證服務器上校驗token信息,
    • 如果不存在access_token 則會繼續執行spring-security的攔截器FilterSecurityInterceptor。 FilterSecurityInterceptor對路徑是否需要授權,已經授權是否通過做校驗
      ~  

解決: 可以采用過濾器在執行到核心過濾器OAuth2AuthenticationProcessingFilter ,將不需要授權的請求頭中的access_token過濾掉。或者APP免登錄接口不傳token

最終采用的是後者

二、Token失效返回的是狀態401的錯誤

1、問題: APP端反饋,傳遞失效access_token,返回401狀態,期望是200同時以錯誤碼方式提示token失效。

排查:經過單步調試分析源碼發現,token失效後,認證服務器會拋出異常,同時響應給資源服務器,資源服務發現認證服務器的錯誤後會拋出InvalideException。

拋出的異常會經過默認的DefaultWebResponseExceptionTranslator 處理然後 Reseponse給Client端。

解決:通過上面的分析指導。最後的異常是在DefaultWebResponseExceptionTranslator 處理的,所以只需要

    • 自定義實現類Implements WebResponseExceptionTranslator 接口處理異常裝換邏輯,
    • 使得自定義的類生效

 (1)自定義異常轉換類

 1 @Slf4j
 2 public class Auth2ResponseExceptionTranslator implements WebResponseExceptionTranslator {
 3 
 4     @Override
 5     public ResponseEntity<OAuth2Exception> translate(Exception e) {
 6         log.error("Auth2異常", e);
 7         Throwable throwable = e.getCause();
 8         if (throwable instanceof InvalidTokenException) {
 9             log.info("token失效:{}", throwable);
10             return new ResponseEntity(new Message<>(ServerConstant.INVALID_TOKEN.getMsg(), ServerConstant.INVALID_TOKEN.getCode()), HttpStatus.OK);
11         }
12         return new ResponseEntity(new Message(e.getMessage(), String.valueOf(HttpStatus.METHOD_NOT_ALLOWED.value())), HttpStatus.METHOD_NOT_ALLOWED);
13     }
14 }

(2)資源服務器中使得自定義類生效

 1 @Configuration
 2 @EnableResourceServer
 3 public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
 4 
 5 
 6   @Override
 7   public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
// 定義異常轉換類生效
8 AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint(); 9 ((OAuth2AuthenticationEntryPoint) authenticationEntryPoint).setExceptionTranslator(new Auth2ResponseExceptionTranslator()); 10 resources.authenticationEntryPoint(authenticationEntryPoint); 11 } 12 13 14 @Override 15 public void configure(HttpSecurity http) throws Exception { 16 http 17 .csrf().disable() 18 .exceptionHandling()
// 定義的不存在access_token時候響應
19 .authenticationEntryPoint(new SecurityAuthenticationEntryPoint()) 20 .and() 21 .authorizeRequests().antMatchers("/**/**").permitAll() 22 .anyRequest().authenticated() 23 .and() 24 .httpBasic().disable(); 25 }

2、問題:測試發現授權接口,當請求參數中不存在access_token時發現接口返回錯誤信息:
{"timestamp":1539337154336,"status":401,"error":"Unauthorized","message":"No message available","path":"/app/businessCode/list"}

排查:經過前面的分析發現,上面提到Security的FilterSecurityInterceptor對OAuth2中返回的信息和本身配置校驗後,拋出AccessDenyException。

解決:經過上面的幾個問題的處理,發現思路還是一樣的,需要定義響應結果,

即1、自定義響應處理邏輯SecurityAuthenticationEntryPoint 2、自定義處理邏輯SecurityAuthenticationEntryPoint生效(見上面的配置)

SecurityAuthenticationEntryPoint具體實現

 1 @Slf4j
 2 public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint {
 3 
 4     @Override
 5     public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
 6        log.error("Spring Securtiy異常", authException);
 7        response.setCharacterEncoding("UTF-8");
 8        response.setContentType("application/json; charset=utf-8");
 9        PrintWriter out = response.getWriter();
10        out.print(JSON.toJSONString(new Message<>(ServerConstant.INVALID_TOKEN.getMsg(), ServerConstant.INVALID_TOKEN.getCode())));
11     }
12 }

  

Spring Security OAuth2 授權失敗(401 問題整理