本文介紹了Spring Security:兩次反序列化請(qǐng)求正文(OAuth2處理)的處理方法,對(duì)大家解決問(wèn)題具有一定的參考價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧!
問(wèn)題描述
這個(gè)問(wèn)題是我使用Spring Security OAuth2庫(kù)所做的一些工作的結(jié)果。我已經(jīng)設(shè)置了一個(gè)OAuth2授權(quán)服務(wù)器和一個(gè)OAuth2資源服務(wù)器,后者用于基于訪問(wèn)令牌進(jìn)行授權(quán)。
問(wèn)題是,訪問(wèn)令牌通常在標(biāo)頭中傳遞,但我們要為其設(shè)置的大客戶端希望在JSON請(qǐng)求正文中傳遞訪問(wèn)令牌。您可以使用一個(gè)接口來(lái)設(shè)置自定義訪問(wèn)令牌提取,但它看起來(lái)如下所示:
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);
}
因此,據(jù)我所知,我只能訪問(wèn)原始HTTPServletRequest,我需要從它反序列化請(qǐng)求并提取訪問(wèn)令牌。
更復(fù)雜的是,請(qǐng)求體還包含處理所需的其他參數(shù),因此我希望將其反序列化為DTO類(lèi),并將其傳遞到控制器中,如下所示:
@RequestMapping("/oauth/someresource")
@Transactional
public Map<String, String> resource(@AuthenticationPrincipal UserDetails userDetails,
@RequestBody ClientRequestDto clientRequestDto) {
// Do some processing based on the request dto
}
我嘗試在令牌提取程序中手動(dòng)反序列化請(qǐng)求,但隨后收到錯(cuò)誤消息”java.lang.IlLegalStateException:已為此請(qǐng)求調(diào)用了getReader()”。
我正在集思廣益,想出一些我可以研究的可能的解決方案,到目前為止,我已經(jīng)想出了:
-
找到重置輸入流的方法
在令牌提取程序中反序列化對(duì)象,將其附加到原始請(qǐng)求對(duì)象,然后只訪問(wèn)控制器中的原始請(qǐng)求對(duì)象,而不是使用@RequestBody
與2類(lèi)似,但找到了添加自定義反序列化程序的方法,該反序列化程序獲取附加到原始請(qǐng)求的對(duì)象,而不是處理請(qǐng)求的輸入流。
無(wú)論如何,這些只是一些想法,如果任何人有任何想法來(lái)優(yōu)雅地解決這個(gè)問(wèn)題,我將非常感激。
編輯:我確實(shí)發(fā)現(xiàn)了這個(gè)類(lèi)似的問(wèn)題:Spring reading request body twice,而上一個(gè)答案確實(shí)有一個(gè)可能的解決方案(創(chuàng)建一個(gè)允許多個(gè)輸入流讀取的修飾器請(qǐng)求類(lèi),并在包裝HttpServletRequest的篩選器鏈的早期創(chuàng)建一個(gè)篩選器)。這似乎是可行的,但有點(diǎn)繁重,所以我把這件事留下來(lái)看看是否有人還有其他想法。
推薦答案
所以我最終找到了另一個(gè)問(wèn)題,這個(gè)問(wèn)題是我在發(fā)帖之前沒(méi)有看到的(How can I read request body multiple times in Spring 'HandlerMethodArgumentResolver'?)。那個(gè)人還建議圍繞HttpServletRequest創(chuàng)建一個(gè)裝飾器,所以我改編了http://www.myjavarecipes.com/how-to-read-post-request-data-twice-in-spring/中的信息,增加了對(duì)大型請(qǐng)求的保護(hù)。
這是我想出來(lái)的,以防任何人有任何反饋:
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;
}
這篇關(guān)于Spring Security:兩次反序列化請(qǐng)求正文(OAuth2處理)的文章就介紹到這了,希望我們推薦的答案對(duì)大家有所幫助,






