本文介紹了Spring Security:兩次反序列化請求正文(OAuth2處理)的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!
問題描述
這個問題是我使用Spring Security 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()”。
我正在集思廣益,想出一些我可以研究的可能的解決方案,到目前為止,我已經想出了:
-
找到重置輸入流的方法
在令牌提取程序中反序列化對象,將其附加到原始請求對象,然后只訪問控制器中的原始請求對象,而不是使用@RequestBody
與2類似,但找到了添加自定義反序列化程序的方法,該反序列化程序獲取附加到原始請求的對象,而不是處理請求的輸入流。
無論如何,這些只是一些想法,如果任何人有任何想法來優雅地解決這個問題,我將非常感激。
編輯:我確實發現了這個類似的問題:Spring reading request body twice,而上一個答案確實有一個可能的解決方案(創建一個允許多個輸入流讀取的修飾器請求類,并在包裝HttpServletRequest的篩選器鏈的早期創建一個篩選器)。這似乎是可行的,但有點繁重,所以我把這件事留下來看看是否有人還有其他想法。
推薦答案
所以我最終找到了另一個問題,這個問題是我在發帖之前沒有看到的(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;
}
這篇關于Spring Security:兩次反序列化請求正文(OAuth2處理)的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,