java - Spring 安全: deserialize request body twice (oauth2 processing)

标签 java spring spring-mvc spring-security spring-security-oauth2

这个问题是我使用 Spring Security Oauth2 库所做的一些工作的结果。我已经设置了一个 oauth2 授权服务器和一个 oauth2 资源服务器,后者用于基于访问 token 进行授权。

问题在于,通常访问 token 是在 header 中传递的,但我们为其设置的大客户端希望在 JSON 请求正文中传递访问 token 。您可以使用一个界面来设置自定义访问 token 提取,但它看起来像这样:

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,我需要从中反序列化请求并提取访问 token 。

然而,让事情变得更加复杂的是,请求正文还包含处理所需的其他参数,因此我想将其反序列化为传递到 Controller 中的 DTO 类,如下所示:

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

我尝试在 token 提取器中手动反序列化请求,但随后收到错误“java.lang.IllegalStateException:getReader() 已为此请求调用”。

我正在集思广益,研究一些可能的解决方案,到目前为止,我已经想出了:

  1. 找到重置输入流的方法
  2. 反序列化 token 提取器中的对象,将其附加到原始请求对象,然后仅访问我的 Controller 中的原始请求对象,而不是使用 @RequestBody
  3. 与 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;
}

关于java - Spring 安全: deserialize request body twice (oauth2 processing),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44168579/

相关文章:

Java Spring 和 Google App Engine

java - @ResponseBody 意外格式

spring-mvc - Spring mockMvc 在使用 ExceptionHandler 时抛出错误

java - 无法追加

java - 在java中追加到xml文件中

java - android 在/dev 中写入文件

java - 如何在lucene中获取文档总数

java - Spring 无法发送 smtp : mail com. sun.mail.smtp.SMTPSendFailedException : 530 5. 7.0 必须先发出 STARTTLS 命令

java - 加密数据库登录信息mySQL连接器

java - 如何从服务器获取html页面中的错误消息