1

這個問題是一些工作,我與春季安全的oauth2庫做的結果。我建立了一個oauth2授權服務器和一個oauth2資源服務器,後者是基於訪問令牌授權的。春季安全:反序列化請求體的兩倍(的oauth2處理)

的問題是正常訪問令牌在頭中傳遞,但大客戶端,我們設置此功能對於想通過訪問令牌的JSON請求主體。還有,你可以用它來設置自定義訪問令牌提取一個接口,但它看起來像這樣:

public interface TokenExtractor { 

/** 
* Extract a token value from an incoming request without authentication. 
* 
* @param request the current ServletRequest 
* @return an authentication token whose principal is an access token (or null if there is none) 
*/ 
Authentication extract(HttpServletRequest request); 
} 

所以,最好的我可以告訴所有我曾訪問是原始的HttpServletRequest,從中我需要反序列化請求並提取訪問令牌。

進一步複雜化的東西,雖然是一個事實,即請求主體還包含加工所需的其他參數,所以我想將它反序列化,我進入我的控制器中的DTO類,像這樣:

@RequestMapping("/oauth/someresource") 
@Transactional 
public Map<String, String> resource(@AuthenticationPrincipal UserDetails userDetails, 
            @RequestBody ClientRequestDto clientRequestDto) { 
// Do some processing based on the request dto 
} 

我試着手動反序列化的標記提取的要求,但後來我得到一個錯誤「java.lang.IllegalStateException:getReader()已經被調用此請求」。

我絞盡腦汁,我可以研究,到目前爲止,我想出了一些可能的解決方案:

  1. 找到一種方法來重新輸入流
  2. 反序列化對象標記提取,其附加到原始請求對象,只是訪問原始請求對象在我的控制器,而不是使用@RequestBody
  3. 像2,但找到一個方法來添加獲取附加到原始請求,而不是對象的自定義解串器處理請求的輸入流。

無論如何,這些只是一些想法,如果任何人有解決這個問題的優雅方式任何想法,我會非常感激。

編輯:我找到了類似的問題:Spring reading request body twice,最後的答案確實有一個可能的解決方案(創建一個裝飾器請求類,允許多個輸入流讀取並在過濾器鏈中創建一個過濾器, HttpServletRequest)。這似乎是可行的,但有點沉重的責任,所以我會留下來看看是否有人有任何其他的想法。

回答

0

所以我最終發現,解決了這個問題,我沒有張貼(How can I read request body multiple times in Spring 'HandlerMethodArgumentResolver'?)之前看到另一個問題。那個人還建議圍繞HttpServletRequest創建一個裝飾器,所以我修改了http://www.myjavarecipes.com/how-to-read-post-request-data-twice-in-spring/的信息,爲大的請求添加了保護。

這就是我想出了,如果任何人有任何反饋:

public class MultiReadHttpServletRequest extends HttpServletRequestWrapper { 
// We include a max byte size to protect against malicious requests, since this all has to be read into memory 
public static final Integer MAX_BYTE_SIZE = 1_048_576; // 1 MB 

private String _body; 

public MultiReadHttpServletRequest(HttpServletRequest request) throws IOException { 
    super(request); 
    _body = ""; 

    InputStream bounded = new BoundedInputStream(request.getInputStream(), MAX_BYTE_SIZE); 
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(bounded)); 

    String line; 
    while ((line = bufferedReader.readLine()) != null){ 
     _body += line; 
    } 
} 

@Override 
public ServletInputStream getInputStream() throws  IOException { 
    final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(_body.getBytes()); 

    return new ServletInputStream() { 
     public int read() throws IOException { 
      return byteArrayInputStream.read(); 
     } 

     @Override 
     public boolean isFinished() { 
      return byteArrayInputStream.available() == 0; 
     } 

     @Override 
     public boolean isReady() { 
      return true; 
     } 

     @Override 
     public void setReadListener(ReadListener readListener) { 

     } 
    }; 
} 

@Override 
public BufferedReader getReader() throws IOException { 
    return new BufferedReader(new InputStreamReader(this.getInputStream())); 
} 
} 

我使用了以下配置:

@Bean 
FilterRegistrationBean multiReadFilter() { 
    FilterRegistrationBean registrationBean = new FilterRegistrationBean(); 
    MultiReadRequestFilter multiReadRequestFilter = new MultiReadRequestFilter(); 
    registrationBean.setFilter(multiReadRequestFilter); 
    registrationBean.setOrder(SecurityProperties.DEFAULT_FILTER_ORDER - 2); 
    registrationBean.setUrlPatterns(Sets.newHashSet("/path/here")); 
    return registrationBean; 
}