3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Spring Boot + Spring SecurityでRequestのBodyを複数回読み取る方法

Posted at

はじめに

Spring Securityの認可処理(投票クラスなど)において、
RequestのBodyを参照しなければならないケースが存在すると思います。
(例 POSTやPUTでDBを更新する場合など

しかし、HttpServletRequestクラスにおいて、
Bodyはストリームであり、読み取りは一度きりとなります。

そこで今回はBodyを複数回読み取る方法を紹介していきます。

やりたいこと

①Bodyから読み取った情報を保持
②Bodyの取得元を①の情報へと差替え

差替え用クラスの準備

Streamクラス
HttpServletRequest.getInputStream()コール時の戻り値の型とし、
初期化にはbyte配列を使用

BufferedServletInputStream.java
public class BufferedServletInputStream extends ServletInputStream {

	private ByteArrayInputStream inputStream;

	// byte配列で初期化
	public BufferedServletInputStream(byte[] buffer) {
		this.inputStream = new ByteArrayInputStream(buffer);
	}

	@Override
	public int available() throws IOException {
		return inputStream.available();
	}

	@Override
	public int read() throws IOException {
		return inputStream.read();
	}

	@Override
	public int read(byte[] b, int off, int len) throws IOException {
		return inputStream.read(b, off, len);
	}

	@Override
	public boolean isFinished() {
		return false;
	}

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

	@Override
	public void setReadListener(ReadListener listener) {

	}
}

HttpServletRequestのラッパークラス

BufferedServletRequestWrapper.java
public class BufferedServletRequestWrapper extends HttpServletRequestWrapper {

	private byte[] buffer;

	public BufferedServletRequestWrapper(HttpServletRequest request) throws IOException {
		super(request);

		// Request BodyからStreamを取得
		InputStream is = request.getInputStream();

		// Streamをbyte配列に変換し、インスタンス変数に保持
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		byte buff[] = new byte[1024];
		int read;
		while ((read = is.read(buff)) > 0) {
			baos.write(buff, 0, read);
		}

		this.buffer = baos.toByteArray();
	}

	// Bodyの取得元をこのメソッドに差替え
	@Override
	public ServletInputStream getInputStream() throws IOException {
		// Streamクラスを初期化して返却
		return new BufferedServletInputStream(this.buffer);
	}
}

実際に差替える

Filterクラス
OncePerRequestFilterにより、リクエスト単位に差替えを実施

MultipleReadEnableFilter.java
@Component
public class MultipleReadEnableFilter extends OncePerRequestFilter {

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

		// HttpServletRequestからラッパークラスを初期化
		HttpServletRequest wrappedRequest = new BufferedServletRequestWrapper((HttpServletRequest) request);
		filterChain.doFilter(wrappedRequest, response);
	}
}

Spring Securityの設定クラスで、作成したFilterを設定

// inputStreamを複数回読み込めるようラップ
http.addFilter(multipleReadEnableFilter);

参考

山奥通信 増刊号

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?