Skip to content

Commit

Permalink
Merge pull request #86 from Akatsuki-USW/develop
Browse files Browse the repository at this point in the history
release: #84 #87
  • Loading branch information
wonslee authored Sep 19, 2023
2 parents 8980572 + d089175 commit 4a8ae38
Show file tree
Hide file tree
Showing 10 changed files with 240 additions and 29 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ dependencies {

// redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

// IOUtils
implementation 'commons-io:commons-io:2.7'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableScheduling;
Expand All @@ -12,6 +13,7 @@
@EnableJpaAuditing
@EnableWebSecurity
@EnableScheduling
@ServletComponentScan
public class BokjakserverApplication {

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ public class GlobalConstants {
/**
* Common
*/
public static final String[] APPOINTED_URIS = {
public static final String[] AUTH_WHITELIST = {
"/",
"/csrf",
"/error",

"/auth/login/admin",
"/auth/reissue",
"/auth/login",
Expand All @@ -26,10 +30,7 @@ public class GlobalConstants {
"/swagger-ui/#",
"/webjars/**",
"/swagger/**",
"/swagger-ui/**",
"/",
"/csrf",
"/error"
"/swagger-ui/**"
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,19 @@
public class GlobalExceptionHandler {

@ExceptionHandler(BuzException.class)
public ResponseEntity<?> handle(BuzException ex) {
public ResponseEntity<?> handleBuzException(BuzException ex) {
log.warn("{}({}) - {}", ex.getClass().getSimpleName(), ex.statusCode.getStatusCode(), ex.getMessage());
return ResponseEntity
.status(ex.statusCode.getHttpCode())
.body(ApiResponse.error(ex.statusCode.getStatusCode(), ex.statusCode.getMessage()));
}

@ExceptionHandler(AuthException.class) //분리 이유: SignToken
public ResponseEntity<?> handle(AuthException ex) {
public ResponseEntity<?> handleAuthException(AuthException ex) {
log.warn("{}({}) - {}", ex.getClass().getSimpleName(), ex.statusCode.getStatusCode(), ex.getMessage());
return ResponseEntity
.status(ex.statusCode.getHttpCode())
.body(ApiResponse.error(ex.statusCode.getStatusCode(), ex.data, ex.statusCode.getMessage()));

}

/**
Expand All @@ -51,7 +50,7 @@ public ResponseEntity<?> handle(AuthException ex) {
// AWS S3 버킷 정책에 맞지 않는 요청
@ExceptionHandler(AwsS3Exception.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
protected ApiResponse<?> handleAwsS3Error(final AwsS3Exception ex) {
protected ApiResponse<?> handleAwsS3Exception(final AwsS3Exception ex) {
log.warn("{} - {}", ex.getClass().getSimpleName(), ex.getMessage());
return error(ex.getAwsS3ErrorCode().getStatusCode(), ex.getMessage());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ public PasswordEncoder passwordEncoder() {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.cors().configurationSource(corsConfigurationSource())
.and()

Expand All @@ -55,37 +58,35 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//세션 사용 안함
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)

.and()
.formLogin().disable()
.httpBasic().disable()

.authorizeHttpRequests()
.requestMatchers(GlobalConstants.APPOINTED_URIS).permitAll()
.anyRequest().authenticated();
.requestMatchers(GlobalConstants.AUTH_WHITELIST).permitAll()
.anyRequest().authenticated()

http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
.and()
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

return http.build();
}

@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(jwtProvider);
return jwtAuthenticationFilter;
return new JwtAuthenticationFilter(jwtProvider);
}

@Bean
public CorsConfigurationSource corsConfigurationSource() { //다시 알아볼것..

CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedOrigins(List.of("*"));
configuration.setAllowedMethods(
Arrays.asList(HttpMethod.POST.name(), HttpMethod.GET.name(),
HttpMethod.PUT.name(), HttpMethod.DELETE.name(),
HttpMethod.OPTIONS.name())
);
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowedHeaders(List.of("*"));

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,8 @@ public ApiResponse<NotificationDto.NotificationListResponse> testCreateNotificat

@GetMapping("/send/email")
public String testSendEmailMySelf() {
long beforeTime = System.currentTimeMillis();
User currentUser = userService.getCurrentUser();
sleepingUserService.sendMail(currentUser.getEmail());
long afterTime = System.currentTimeMillis();
long diffTime = afterTime-beforeTime;
System.out.println("실행 시간: " + diffTime);
return "good";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,11 @@ public class CustomSliceExecutionUtils {
public static <T> Slice<T> getSlice(List<T> content, Pageable pageable) {
boolean hasNext = false;

System.out.println("content.size() = " + content.size());
System.out.println("pageable.getPageSize() = " + pageable.getPageSize());

if (content.size() > pageable.getPageSize()) { // content.size가 최대일 경우: 항상 page size + 1 이고 다음 레코드가 있다.
content.remove(pageable.getPageSize()); // limit걸 때 +1 했던 마지막 레코드를 삭제
hasNext = true;
}

System.out.println("hasNext = " + hasNext);

return new SliceImpl<>(content, pageable, hasNext);
}

Expand Down
51 changes: 51 additions & 0 deletions src/main/java/bokjak/bokjakserver/web/log/LoggerAspect.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package bokjak.bokjakserver.web.log;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.json.simple.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Enumeration;
import java.util.Objects;

@Component
@Aspect
@Slf4j
public class LoggerAspect {
@Pointcut("execution(* bokjak.bokjakserver..*Controller.*(..)) || execution(* bokjak.bokjakserver..*GlobalExceptionHandler.*(..))")
// 이런 패턴이 실행될 경우 수행
public void loggerPointCut() {
}

@Around("loggerPointCut()")
public Object logRequestUri(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object result = proceedingJoinPoint.proceed();
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

if (Objects.nonNull(requestAttributes)) {
HttpServletRequest request = requestAttributes.getRequest(); // request 정보를 가져온다.
String controllerName = proceedingJoinPoint.getSignature().getDeclaringType().getSimpleName();
String methodName = proceedingJoinPoint.getSignature().getName();
log.info("{}.{}: {} {} PARAM={}", controllerName, methodName, request.getMethod(), request.getRequestURI(), extractParams(request)); // param에 담긴 정보들을 한번에 로깅한다.
}

return result;
}

private static JSONObject extractParams(HttpServletRequest request) { // request로부터 param 추출, JSONObject로 변환
JSONObject jsonObject = new JSONObject();
Enumeration<String> params = request.getParameterNames();
while (params.hasMoreElements()) {
String param = params.nextElement();
String replaceParam = param.replaceAll("\\.", "-");
jsonObject.put(replaceParam, request.getParameter(param));
}
return jsonObject;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package bokjak.bokjakserver.web.log;

import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
public class ReadableRequestBodyWrapper extends HttpServletRequestWrapper {
private final Charset encoding;
private byte[] rawData;
private final Map<String, String[]> params = new HashMap<>();

public ReadableRequestBodyWrapper(HttpServletRequest request) {
super(request);
this.params.putAll(request.getParameterMap()); // 오리지널 요청의 파라미터들 저장

String charEncoding = request.getCharacterEncoding(); // 인코딩 설정
this.encoding = StringUtils.isBlank(charEncoding) ? StandardCharsets.UTF_8 : Charset.forName(charEncoding);

try {
// 중요: body가 유실되지 않도록 함. getInputStream -> rawData에 저장 -> getReader() 에서 새 스트림으로 생성
InputStream inputStream = request.getInputStream();
this.rawData = IOUtils.toByteArray(inputStream);

// body 파싱
String collect = this.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
if (StringUtils.isEmpty(collect)) { // body 가 없을경우 종료
return;
}
if (request.getContentType() != null && request.getContentType().contains(
ContentType.MULTIPART_FORM_DATA.getMimeType())) { // 파일 업로드시 로깅 제외
return;
}

JSONParser jsonParser = new JSONParser();
Object parse = jsonParser.parse(collect);
if (parse instanceof JSONArray) {
JSONArray jsonArray = (JSONArray) jsonParser.parse(collect);
setParameter("requestBody", jsonArray.toJSONString());
} else {
JSONObject jsonObject = (JSONObject) jsonParser.parse(collect);
for (Object key : jsonObject.keySet()) {
setParameter(key.toString(), jsonObject.get(key.toString()).toString().replace("\"", "\\\""));
}
}
} catch (Exception e) {
log.error("ReadableRequestWrapper init error", e);
}
}

@Override
public String getParameter(String name) {
String[] paramArray = getParameterValues(name);
if (paramArray != null && paramArray.length > 0) {
return paramArray[0];
} else {
return null;
}
}

@Override
public Map<String, String[]> getParameterMap() {
return Collections.unmodifiableMap(params);
}

@Override
public Enumeration<String> getParameterNames() {
return Collections.enumeration(params.keySet());
}

@Override
public String[] getParameterValues(String name) {
String[] result = null;
String[] dummyParamValue = params.get(name);

if (dummyParamValue != null) {
result = new String[dummyParamValue.length];
System.arraycopy(dummyParamValue, 0, result, 0, dummyParamValue.length);
}
return result;
}

public void setParameter(String name, String value) {
String[] param = {value};
setParameter(name, param);
}

public void setParameter(String name, String[] values) {
params.put(name, values);
}

@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.rawData);

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

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

@Override
public void setReadListener(ReadListener readListener) {
// Do nothing
}

public int read() {
return byteArrayInputStream.read();
}
};
}

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package bokjak.bokjakserver.web.log;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.NotNull;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@WebFilter(urlPatterns = "/*") // 대상: 전체 URI
public class ReadableRequestBodyWrapperFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(new ReadableRequestBodyWrapper(request), response); // 필터 체인에 커스텀 Wrapper 추가
}

}

0 comments on commit 4a8ae38

Please sign in to comment.