Explorar o código

메뉴접속이력 생성 로직 추가

gagamel %!s(int64=5) %!d(string=hai) anos
pai
achega
f7e81f1e4c

+ 22 - 0
src/main/java/com/style24/admin/biz/service/TsaSystemService.java

@@ -16,6 +16,7 @@ import com.style24.persistence.domain.Batch;
 import com.style24.persistence.domain.BatchLog;
 import com.style24.persistence.domain.CommonCode;
 import com.style24.persistence.domain.Menu;
+import com.style24.persistence.domain.MenuAccessHst;
 import com.style24.persistence.domain.MenuRole;
 import com.style24.persistence.domain.SampleFile;
 import com.style24.persistence.domain.User;
@@ -328,6 +329,27 @@ public class TsaSystemService {
 		}
 	}
 
+	/**
+	 * 메뉴접속이력 생성
+	 * @param menuAccessHst - 메뉴접속 정보
+	 * @author gagamel
+	 * @since 2020. 10. 7
+	 */
+	public void createMenuAccessHistory(String menuId, String reqUrl, String params) {
+		MenuAccessHst menuAccessHst = new MenuAccessHst();
+		menuAccessHst.setUserNo(TsaSession.getInfo().getUserNo());
+		menuAccessHst.setMenuId(menuId);
+		menuAccessHst.setReqUrl(reqUrl);
+
+		if (StringUtils.isNotBlank(params)) {
+			menuAccessHst.setParams(params);
+		}
+
+		menuAccessHst.setRegNo(TsaSession.getInfo().getUserNo());
+
+		systemDao.createMenuAccessHistory(menuAccessHst);
+	}
+
 	/**
 	 * 공통코드 목록
 	 * @return

+ 16 - 0
src/main/java/com/style24/admin/support/config/TsaWebMvcConfig.java

@@ -15,6 +15,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 import com.style24.admin.support.interceptor.TsaDefaultInterceptor;
 import com.style24.admin.support.interceptor.TsaPosInterceptor;
+import com.style24.admin.support.readable.TsaRequestFilter;
 
 import com.gagaframework.web.core.filter.GagaXssServletFilter;
 import com.gagaframework.web.rest.client.GagaRequestStringTrim;
@@ -59,6 +60,21 @@ public class TsaWebMvcConfig implements WebMvcConfigurer {
 		return new GagaRequestStringTrim();
 	}
 
+	/**
+	 * Readable Request Filter
+	 *
+	 * @return
+	 */
+	@SuppressWarnings({"rawtypes", "unchecked"})
+	@Bean
+	public FilterRegistrationBean readableRequestFilterRegistrationBean() {
+		FilterRegistrationBean bean = new FilterRegistrationBean();
+		bean.setFilter(new TsaRequestFilter());
+		bean.setOrder(3);
+		bean.addUrlPatterns("/*");
+		return bean;
+	}
+
 	/**
 	 * XSS(Cross Site Script) Prevention Filter
 	 *

+ 32 - 1
src/main/java/com/style24/admin/support/interceptor/TsaDefaultInterceptor.java

@@ -1,19 +1,26 @@
 package com.style24.admin.support.interceptor;
 
+import java.nio.charset.StandardCharsets;
+
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.web.servlet.ModelAndView;
 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
+import com.style24.admin.biz.service.TsaSystemService;
 import com.style24.admin.support.security.session.TsaSession;
 import com.style24.core.support.exception.TscNoSessionException;
 import com.style24.core.support.message.TscMessageByLocale;
 
 import lombok.extern.slf4j.Slf4j;
 
+import com.gagaframework.web.util.GagaCookieUtil;
+
 /**
  * 모든 Request에 대해 세션 확인
  * 
@@ -27,6 +34,9 @@ public class TsaDefaultInterceptor extends HandlerInterceptorAdapter {
 	@Autowired
 	private TscMessageByLocale message;
 
+	@Autowired
+	private TsaSystemService systemService;
+
 	@Override
 	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
 		log.info("request.isSecure(): [{}]", request.isSecure());
@@ -58,7 +68,28 @@ public class TsaDefaultInterceptor extends HandlerInterceptorAdapter {
 	@Override
 	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
 		ModelAndView modelAndView) throws Exception {
-		// Do nothing
+		// 쿠키로 설정된 메뉴ID
+		String ckMenuId = GagaCookieUtil.getCookie(request, "CK_MENU_ID");
+		log.info("ckMenuId: {}", ckMenuId);
+
+		// 메뉴ID 쿠키가 있으면
+		if (StringUtils.isNotBlank(ckMenuId)) {
+			// Request URL with QueryString
+			String reqUrl = request.getRequestURL().toString();
+			if (StringUtils.isNotBlank(request.getQueryString())) {
+				reqUrl += "?" + request.getQueryString();
+			}
+
+			// RequestBody는 request.getInputStream()을 통해 한 번만 읽을 수 있다.
+			// Controller 또는 Interceptor에서 계속 액세스 하려면
+			// JSON을 POJO 객체로 역직렬화 할 수 있도록 HttpServletRequestWrapper 확장해서 구현하고 TsaRequestFilter로 구성하여
+			// TsaWebMvcConfig에 Filter를 등록한 후에 아래와 같이 가져오면 된다.
+			String params = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
+			log.info("params: {}", params);
+
+			// 메뉴접속이력 생성
+			systemService.createMenuAccessHistory(ckMenuId, reqUrl, (params.isEmpty() ? "" : params.toString()));
+		}
 	}
 
 }

+ 64 - 0
src/main/java/com/style24/admin/support/readable/TsaHttpServletRequestWrapper.java

@@ -0,0 +1,64 @@
+package com.style24.admin.support.readable;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+import org.apache.commons.io.IOUtils;
+
+/**
+ * RequestBody는 request.getInputStream()을 통해 한 번만 읽을 수 있다.
+ * Controller 또는 Interceptor에서 계속 액세스 하기 위해 JSON을 POJO 객체로 역직렬화 할 수 있도록 구현한 HttpServletRequestWrapper
+ * 
+ * @author gagamel
+ * @since 2021. 4. 20
+ */
+public class TsaHttpServletRequestWrapper extends HttpServletRequestWrapper {
+
+	private byte[] rawData = {};
+	private HttpServletRequest request;
+	private TsaServletInputStream servletStream;
+
+	public TsaHttpServletRequestWrapper(HttpServletRequest request) {
+		super(request);
+		this.request = request;
+		this.servletStream = new TsaServletInputStream();
+	}
+
+	public void resetInputStream() throws IOException {
+		initRawData();
+		servletStream.inputStream = new ByteArrayInputStream(rawData);
+	}
+
+	private void initRawData() throws IOException {
+		if (rawData.length == 0) {
+			byte[] b = IOUtils.toByteArray(this.request.getInputStream());
+			if (b != null)
+				rawData = b;
+		}
+
+		servletStream.inputStream = new ByteArrayInputStream(rawData);
+	}
+
+	@Override
+	public ServletInputStream getInputStream() throws IOException {
+		initRawData();
+		return servletStream;
+	}
+
+	public BufferedReader getReader() throws IOException {
+		initRawData();
+		String encoding = getCharacterEncoding();
+		if (encoding != null) {
+			return new BufferedReader(new InputStreamReader(servletStream, encoding));
+		} else {
+			return new BufferedReader(new InputStreamReader(servletStream));
+		}
+	}
+
+}

+ 64 - 0
src/main/java/com/style24/admin/support/readable/TsaRequestFilter.java

@@ -0,0 +1,64 @@
+package com.style24.admin.support.readable;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.http.HttpServletRequest;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 다시 읽을 수 있는 Request Filter
+ * 		RequestBody는 request.getInputStream()을 통해 한 번만 읽을 수 있다.
+ * 		Controller 또는 Interceptor에서 계속 액세스 하기 위해 구현한 Request Filter
+ * 
+ * @author gagamel
+ * @since 2021. 4. 20
+ */
+@WebFilter("/readableRequestFilter")
+@Slf4j
+public class TsaRequestFilter implements Filter {
+
+	/**
+	 * Default constructor. 
+	 */
+	public TsaRequestFilter() {
+		// TODO Auto-generated constructor stub
+	}
+
+	/**
+	 * @see Filter#destroy()
+	 */
+	public void destroy() {
+		// TODO Auto-generated method stub
+	}
+
+	/**
+	 * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
+	 */
+	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+		TsaHttpServletRequestWrapper wrappedRequest = null;
+
+		try {
+			wrappedRequest = new TsaHttpServletRequestWrapper((HttpServletRequest)request);
+		} catch (Exception e) {
+			log.error("Fail to wrap request", e);
+		}
+
+		chain.doFilter(wrappedRequest, response);
+	}
+
+	/**
+	 * @see Filter#init(FilterConfig)
+	 */
+	public void init(FilterConfig fConfig) throws ServletException {
+		// TODO Auto-generated method stub
+	}
+
+}

+ 114 - 0
src/main/java/com/style24/admin/support/readable/TsaServletInputStream.java

@@ -0,0 +1,114 @@
+package com.style24.admin.support.readable;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+
+/**
+ * RequestBody는 request.getInputStream()을 통해 한 번만 읽을 수 있다.
+ * Controller 또는 Interceptor에서 계속 액세스 하기 위해 커스터마이징한 ServletInputStream
+ * 
+ * @author gagamel
+ * @since 2021. 4. 20
+ */
+public class TsaServletInputStream extends ServletInputStream {
+
+	public InputStream inputStream;
+
+	private ServletInputStream servletInputStream = new ServletInputStream() {
+		boolean isFinished = false;
+		boolean isReady = true;
+		ReadListener readListener = null;
+
+		public int read() throws IOException {
+			int i = inputStream.read();
+			isFinished = i == -1;
+			isReady = !isFinished;
+			return i;
+		}
+
+		@Override
+		public boolean isFinished() {
+			return isFinished;
+		}
+
+		@Override
+		public boolean isReady() {
+			return isReady;
+		}
+
+		@Override
+		public void setReadListener(ReadListener readListener) {
+			this.readListener = readListener;
+		}
+	};
+
+	public int available() throws IOException {
+		return inputStream.available();
+	}
+
+	public void close() throws IOException {
+		inputStream.close();
+	}
+
+	public void mark(int readLimit) {
+		inputStream.mark(readLimit);
+	}
+
+	public boolean markSupported() {
+		return inputStream.markSupported();
+	}
+
+	public int read(byte[] b, int off, int len) throws IOException {
+		return inputStream.read(b, off, len);
+	}
+
+	public int readline(byte[] b, int off, int len) throws IOException {
+		if (len <= 0) {
+			return 0;
+		}
+
+		int count = 0, c;
+
+		while ((c = read()) != -1) {
+			b[off++] = (byte)c;
+			count++;
+			if (c == '\n' || count == len) {
+				break;
+			}
+		}
+
+		return count > 0 ? count : -1;
+	}
+
+	public void reset() throws IOException {
+		inputStream.reset();
+	}
+
+	public long skip(long n) throws IOException {
+		return inputStream.skip(n);
+	}
+
+	@Override
+	public int read() throws IOException {
+		return inputStream.read();
+	}
+
+	@Override
+	public void setReadListener(ReadListener listener) {
+		servletInputStream.setReadListener(listener);
+	}
+
+	@Override
+	public boolean isReady() {
+		return servletInputStream.isReady();
+	}
+
+	@Override
+	public boolean isFinished() {
+		return servletInputStream.isFinished();
+	}
+
+}

+ 1 - 1
src/main/java/com/style24/persistence/domain/MenuAccessHst.java

@@ -15,7 +15,7 @@ import lombok.Data;
 public class MenuAccessHst extends TscBaseDomain {
 
 	private Integer menuAccessSq;
-	private String userNo;
+	private Integer userNo;
 	private String menuId;
 	private String reqUrl;
 	private String params;

+ 1 - 0
src/main/webapp/ux/js/admin.common.js

@@ -26,6 +26,7 @@ let cfnGetMenuInfo = function(menuId) {
 	loginMenuList.forEach(function(item) {
 		if (menuId == item.menuId) {
 			menu = item;
+			gagajf.setCookie("CK_MENU_ID", menuId, 1); // 쿠키 설정
 			return false;
 		}
 	});