繁体   English   中英

Spring Security:对请求体进行两次反序列化(oauth2处理)

[英]Spring Security: deserialize request body twice (oauth2 processing)

这个问题是我正在使用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()已经为此请求调用”。

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

  1. 找到重置输入流的方法
  2. 反序列化Token Extractor中的对象,将其附加到原始请求对象,只需访问控制器中的原始请求对象,而不是使用@RequestBody
  3. 像2,但找到一种方法来添加一个自定义反序列化器,它取出附加到原始请求的对象,而不是处理请求的输入流。

无论如何,这些只是一些想法,如果任何人有一个优雅的解决方法的想法,我会非常感激。

编辑:我确实发现了这个类似的问题: Spring读取请求体两次 ,最后一个答案确实有一个可能的解决方案(创建一个装饰器请求类,允许多个输入流读取并在包装的过滤器链中创建过滤器HttpServletRequest)。 它似乎可行,但有点重,所以我会留下来看看是否还有其他任何想法。

所以我最终找到了另一个问题,解决了我在发布之前没有看到的这个问题( 如何在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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM