RequestBody여러번 사용하기

김신영·2024년 7월 15일

Spring

목록 보기
3/5
post-thumbnail

프로젝트에서 request의 특정 값에 따라 JWT key가 달라져야 하는 부분이 있었는데 필터단에서 Body부분의 값을 확인하고 jwt검증까지는 완료했으나 정작 contoller단에서 body부분이 빈값으로 들어오는 상황이 있어서 혹시 나와같은 고민을 하는사람에게 도움이 될까 정리한다
검색하다보니 getInputStream()을 이용해서 인터셉터 단이나 필터단에서 body를 이미 소비하였기 때문에 controller에서 값이 비어져있음을 알게되었고 해결방법을 작성한다.

import org.apache.commons.io.IOUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * HttpServletRequest 의 body부분을 여러번 사용하기 위해 만든 클래스
 *
 * 2023/09/05
 * 김신영
 */
public class RereadableRequestWrapper extends HttpServletRequestWrapper {

    private final Charset encoding;
    private byte[] rawData;

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

        String characterEncoding = request.getCharacterEncoding();
        if (characterEncoding.equals("")) {
            characterEncoding = StandardCharsets.UTF_8.name();
        }
        this.encoding = Charset.forName(characterEncoding);

        try {
            InputStream inputStream = request.getInputStream();
            this.rawData = IOUtils.toByteArray(inputStream);
        } catch (IOException e) {
            throw e;
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.rawData);
        ServletInputStream servletInputStream = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

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

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream(), this.encoding));
    }

    @Override
    public ServletRequest getRequest() {
        return super.getRequest();
    }
}

실사용부분

@Override
	public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
		RereadableRequestWrapper rereadableRequestWrapper = new RereadableRequestWrapper(request);

		String parameter = null;
		for (String name : Collections.<String>list(request.getParameterNames())) {
			String value = request.getParameter(name);
			parameter += name + "=" + value + "&";
		}


		String serviceName;

		try {
			String bodyString = getRequestBodyToString(rereadableRequestWrapper);
			Gson gson = new Gson();
			Map bodyMap = gson.fromJson(bodyString, Map.class);

			if (bodyMap.get("serviceName") != null || request.getParameter("serviceName") != null) {
				serviceName = bodyMap.get("serviceName") != null ? (String) bodyMap.get("serviceName") : request.getParameter("serviceName");
			} else {
				serviceName = null;
			}

		} catch (Exception e) {
			throw new ServletException(e);
		}

		String header = request.getHeader(HEADER_STRING);

		if (header == null || !header.startsWith(TOKEN_PREFIX)) {
			chain.doFilter(rereadableRequestWrapper, response);
			return;
		}

		// Authorization의 Bearer를 제거하고 JWT 토큰을 가져온다.
		String jwt = header.replace(TOKEN_PREFIX, "");
		if (jwt != null) {
			SecurityContextHolder.getContext().setAuthentication(jwtTokenProvider.getAuthentication(jwt, serviceName));
			//SecurityContextHolder.getContext().setAuthentication(jwtTokenProvider.getAuthentication(jwt, "sws-chatting"));
		}

		chain.doFilter(rereadableRequestWrapper, response);
	}

RereadableRequestWrapper 클래스를 만들어주고 contoller보다 앞단에서 body의 내용이 필요할때 사용하면 된다.

참고자료 : https://velog.io/@saint6839/Controller%EC%97%90%EC%84%9C-HttpRequest-Body-%EA%B0%92%EC%9D%80-%EC%99%9C-%EB%B9%84%EC%9B%8C%EC%A0%B8-%EC%9E%88%EC%9D%84%EA%B9%8C

profile
공부합시다.

0개의 댓글