Skip to content

Commit

Permalink
πŸŽ‰ init: ν”„λ‘œμ νŠΈ 생성
Browse files Browse the repository at this point in the history
  • Loading branch information
orijoon98 committed May 2, 2023
0 parents commit 4671afc
Show file tree
Hide file tree
Showing 61 changed files with 1,706 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
name: feature issue about: "이슈 갈 겨 ~~~~~ \U0001F3C4‍♂️"
title: ''
labels: ''
assignees: ''
---

## πŸ“Œ Feature Issue
<!-- κ΅¬ν˜„ν•  κΈ°λŠ₯에 λŒ€ν•œ λ‚΄μš©μ„ μ„€λͺ…ν•΄μ£Όμ„Έμš”. -->

## πŸ“ To-do
<!-- ν•΄μ•Ό ν•  일듀을 μ μ–΄μ£Όμ„Έμš”. -->

- [ ]
- [ ]
11 changes: 11 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## βœ’οΈ κ΄€λ ¨ 이슈번호

- Closes #536

## πŸ”‘ Key Changes

1. λ‚΄μš©
- μ„€λͺ…

## πŸ“’ To Reviewers
-
42 changes: 42 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/

### VS Code ###
.vscode/

application.yml
application-local.yml
application-dev.yml
application-prod.yml
9 changes: 9 additions & 0 deletions blossom-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# muyaho-api

> μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λͺ¨λ“ˆ 계측 (API)
## ν•˜μœ„ λͺ¨λ“ˆ

- blossom-common
- blossom-domain
- blossom-external
14 changes: 14 additions & 0 deletions blossom-api/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
dependencies {
implementation project(':blossom-domain')
implementation project(':blossom-external')

// spring mvc
implementation 'org.springframework.boot:spring-boot-starter-web'

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

// swagger
implementation 'org.springdoc:springdoc-openapi-ui:1.5.4'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.seoultech.blossom.api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.seoultech.blossom.domain.BlossomDomainRoot;
import com.seoultech.blossom.external.BlossomExternalRoot;

@SpringBootApplication(scanBasePackageClasses = {
BlossomDomainRoot.class,
BlossomExternalRoot.class
})
public class ApiApplication {

public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.seoultech.blossom.api.config;

import java.util.List;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.seoultech.blossom.api.config.interceptor.auth.AuthInterceptor;
import com.seoultech.blossom.api.config.resolver.UserIdResolver;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Configuration
public class WebConfig implements WebMvcConfigurer {

private final AuthInterceptor authInterceptor;
private final UserIdResolver userIdResolver;

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS");
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor);
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(userIdResolver);
}

@Bean
public MessageSource validationMessageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/messages/validation");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}

@Override
public Validator getValidator() {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(validationMessageSource());
return bean;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.seoultech.blossom.api.config.filter;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
public class FilterConfig {

@Profile({"dev", "prod"})
@Bean
public FilterRegistrationBean<RequestLoggingFilter> requestLoggingFilter() {
FilterRegistrationBean<RequestLoggingFilter> filter = new FilterRegistrationBean<>(new RequestLoggingFilter());
filter.addUrlPatterns("/api/*");
filter.setOrder(1);
return filter;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.seoultech.blossom.api.config.filter;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class RequestLoggingFilter implements Filter {

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws
IOException,
ServletException {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest)request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(
(HttpServletResponse)response);

long start = System.currentTimeMillis();
chain.doFilter(requestWrapper, responseWrapper);
long end = System.currentTimeMillis();

log.info("\n" +
"[REQUEST] {} - {} {} - {}s\n" +
"Headers : {}\n" +
"Request : {}\n" +
"Response : {}\n",
((HttpServletRequest)request).getMethod(), ((HttpServletRequest)request).getRequestURI(),
responseWrapper.getStatus(), (end - start) / 1000.0,
getHeaders((HttpServletRequest)request),
getRequestBody(requestWrapper),
getResponseBody(responseWrapper));
}

private Map<String, String> getHeaders(HttpServletRequest request) {
Map<String, String> headerMap = new HashMap<>();

Enumeration<String> headerArray = request.getHeaderNames();
while (headerArray.hasMoreElements()) {
String headerName = headerArray.nextElement();
headerMap.put(headerName, request.getHeader(headerName));
}
return headerMap;
}

private String getRequestBody(ContentCachingRequestWrapper request) {
ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
if (wrapper != null) {
byte[] buf = wrapper.getContentAsByteArray();
if (buf.length > 0) {
try {
return new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
} catch (UnsupportedEncodingException e) {
return " - ";
}
}
}
return " - ";
}

private String getResponseBody(final HttpServletResponse response) throws IOException {
String payload = null;
ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response,
ContentCachingResponseWrapper.class);
if (wrapper != null) {
byte[] buf = wrapper.getContentAsByteArray();
if (buf.length > 0) {
payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
wrapper.copyBodyToResponse();
}
}
return payload == null ? " - " : payload;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.seoultech.blossom.api.config.interceptor.auth;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Auth {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.seoultech.blossom.api.config.interceptor.auth;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import com.seoultech.blossom.common.constant.JwtKey;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Component
public class AuthInterceptor implements HandlerInterceptor {

private final LoginCheckHandler loginCheckHandler;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod)handler;
Auth auth = handlerMethod.getMethodAnnotation(Auth.class);
if (auth == null) {
return true;
}
Long userId = loginCheckHandler.getUserId(request);
request.setAttribute(JwtKey.USER_ID, userId);
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.seoultech.blossom.api.config.interceptor.auth;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.seoultech.blossom.common.exception.UnAuthorizedException;
import com.seoultech.blossom.common.util.JwtUtils;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Component
public class LoginCheckHandler {

private final JwtUtils jwtUtils;

public Long getUserId(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
String accessToken = bearerToken.substring("Bearer ".length());
if (jwtUtils.validateToken(accessToken)) {
Long userId = jwtUtils.getUserIdFromJwt(accessToken);
if (userId != null) {
return userId;
}
}
}
throw new UnAuthorizedException(String.format("잘λͺ»λœ JWT (%s) μž…λ‹ˆλ‹€.", bearerToken));
}
}
Loading

0 comments on commit 4671afc

Please sign in to comment.