From 315db454693fe3fcd4de06e857294403fa64b565 Mon Sep 17 00:00:00 2001 From: RyuKwanKon Date: Wed, 6 Sep 2023 21:14:52 +0900 Subject: [PATCH 01/14] =?UTF-8?q?[fix]=20#5=20=EC=98=A4=ED=83=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hdmedi_server/global/common/HealthCheckApiController.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/com/kusithm/hdmedi_server/global/common/HealthCheckApiController.java b/src/main/java/com/kusithm/hdmedi_server/global/common/HealthCheckApiController.java index 6c6840c..59eaa60 100644 --- a/src/main/java/com/kusithm/hdmedi_server/global/common/HealthCheckApiController.java +++ b/src/main/java/com/kusithm/hdmedi_server/global/common/HealthCheckApiController.java @@ -6,7 +6,5 @@ @RestController public class HealthCheckApiController { @RequestMapping("/") - public String baggle() { - return "HDmedi Kusithm 2조!"; - } + public String hdmedi() { return "HDmedi Kusithm 2조!"; } } From 34d7f5e62409d027d418972048a0e9a0f6911152 Mon Sep 17 00:00:00 2001 From: RyuKwanKon Date: Wed, 6 Sep 2023 21:26:00 +0900 Subject: [PATCH 02/14] =?UTF-8?q?[chore]=20#5=20security=20&=20JPA=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=ED=99=9C=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 67102a2..8c329ff 100644 --- a/build.gradle +++ b/build.gradle @@ -22,14 +22,26 @@ repositories { } dependencies { -// implementation 'org.springframework.boot:spring-boot-starter-data-jpa' -// implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' implementation 'org.springframework.boot:spring-boot-starter-web' - compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' - runtimeOnly 'com.mysql:mysql-connector-j' - annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' + + // spring security + implementation 'org.springframework.boot:spring-boot-starter-security' + + // lombok + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + // jwt + implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.2' + implementation group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.2' + implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2' + + // mysql + runtimeOnly 'com.mysql:mysql-connector-j' } tasks.named('test') { From 9a743d76d5d70b143a5b589ec44e93bf558d6886 Mon Sep 17 00:00:00 2001 From: RyuKwanKon Date: Wed, 6 Sep 2023 23:52:27 +0900 Subject: [PATCH 03/14] =?UTF-8?q?[feat]=20#5=20access=20token=20&=20refres?= =?UTF-8?q?h=20token=20error=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hdmedi_server/global/error/exception/ErrorCode.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/com/kusithm/hdmedi_server/global/error/exception/ErrorCode.java b/src/main/java/com/kusithm/hdmedi_server/global/error/exception/ErrorCode.java index 0b2bd8f..7d53b30 100644 --- a/src/main/java/com/kusithm/hdmedi_server/global/error/exception/ErrorCode.java +++ b/src/main/java/com/kusithm/hdmedi_server/global/error/exception/ErrorCode.java @@ -12,6 +12,13 @@ public enum ErrorCode { * 400 Bad Request */ BAD_REQUEST(HttpStatus.BAD_REQUEST, "잘못된 요청입니다."), + INVALID_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, "액세스 토큰의 형식이 올바르지 않습니다. Bearer 타입을 확인해 주세요."), + INVALID_ACCESS_TOKEN_VALUE(HttpStatus.UNAUTHORIZED, "액세스 토큰의 값이 올바르지 않습니다."), + EXPIRED_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, "액세스 토큰이 만료되었습니다. 재발급 받아주세요."), + INVALID_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "리프레시 토큰의 형식이 올바르지 않습니다."), + INVALID_REFRESH_TOKEN_VALUE(HttpStatus.UNAUTHORIZED, "리프레시 토큰의 값이 올바르지 않습니다."), + EXPIRED_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "리프레시 토큰이 만료되었습니다. 다시 로그인해 주세요."), + NOT_MATCH_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "일치하지 않는 리프레시 토큰입니다."), /** * 401 Unauthorized From 808b528b2a34acff4998c04ecd529b9b8a8561b0 Mon Sep 17 00:00:00 2001 From: RyuKwanKon Date: Wed, 6 Sep 2023 23:53:22 +0900 Subject: [PATCH 04/14] =?UTF-8?q?[feat]=20#5=20JWT=20Provider=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/jwt/JwtProvider.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/main/java/com/kusithm/hdmedi_server/global/config/jwt/JwtProvider.java diff --git a/src/main/java/com/kusithm/hdmedi_server/global/config/jwt/JwtProvider.java b/src/main/java/com/kusithm/hdmedi_server/global/config/jwt/JwtProvider.java new file mode 100644 index 0000000..e45b12c --- /dev/null +++ b/src/main/java/com/kusithm/hdmedi_server/global/config/jwt/JwtProvider.java @@ -0,0 +1,83 @@ +package com.kusithm.hdmedi_server.global.config.jwt; + +import com.kusithm.hdmedi_server.global.error.exception.ErrorCode; +import com.kusithm.hdmedi_server.global.error.exception.UnauthorizedException; +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.security.Key; +import java.util.Base64; +import java.util.Date; + +@Getter +@Component +public class JwtProvider { + @Value("${jwt.secret}") + private String secretKey; + @Value("${jwt.access-token-expire-time}") + private long ACCESS_TOKEN_EXPIRE_TIME; + @Value("${jwt.refresh-token-expire-time}") + private long REFRESH_TOKEN_EXPIRE_TIME; + + public Token issueToken(Long userId) { + return Token.of(generateToken(userId, true), generateToken(userId, false)); + } + + public void validateAccessToken(String accessToken) { + try { + getJwtParser().parseClaimsJws(accessToken); + } catch (ExpiredJwtException e) { + throw new UnauthorizedException(ErrorCode.EXPIRED_ACCESS_TOKEN); + } catch (UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) { + throw new UnauthorizedException(ErrorCode.INVALID_ACCESS_TOKEN_VALUE); + } + } + + public void validateRefreshToken(String refreshToken) { + try { + getJwtParser().parseClaimsJws(refreshToken); + } catch (ExpiredJwtException e) { + throw new UnauthorizedException(ErrorCode.EXPIRED_REFRESH_TOKEN); + } catch (UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) { + throw new UnauthorizedException(ErrorCode.INVALID_REFRESH_TOKEN_VALUE); + } + } + + public void equalsRefreshToken(String providedRefreshToken, String storedRefreshToken) { + if (!providedRefreshToken.equals(storedRefreshToken)) { + throw new UnauthorizedException(ErrorCode.NOT_MATCH_REFRESH_TOKEN); + } + } + + public Long getSubject(String token) { + return Long.valueOf(getJwtParser().parseClaimsJws(token) + .getBody() + .getSubject()); + } + + private String generateToken(Long userId, boolean isAccessToken) { + final Date now = new Date(); + final Date expiration = new Date(now.getTime() + (isAccessToken ? ACCESS_TOKEN_EXPIRE_TIME : REFRESH_TOKEN_EXPIRE_TIME)); + return Jwts.builder() + .setHeaderParam(Header.TYPE, Header.JWT_TYPE) + .setSubject(String.valueOf(userId)) + .setIssuedAt(now) + .setExpiration(expiration) + .signWith(getSigningKey(), SignatureAlgorithm.HS256) + .compact(); + } + + private JwtParser getJwtParser() { + return Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build(); + } + + private Key getSigningKey() { + String encoded = Base64.getEncoder().encodeToString(secretKey.getBytes()); + return Keys.hmacShaKeyFor(encoded.getBytes()); + } +} From 3fc0da70f33f704901da42058254d991f182d3f5 Mon Sep 17 00:00:00 2001 From: RyuKwanKon Date: Wed, 6 Sep 2023 23:53:34 +0900 Subject: [PATCH 05/14] =?UTF-8?q?[feat]=20#5=20JWT=20Token=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hdmedi_server/global/config/jwt/Token.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/java/com/kusithm/hdmedi_server/global/config/jwt/Token.java diff --git a/src/main/java/com/kusithm/hdmedi_server/global/config/jwt/Token.java b/src/main/java/com/kusithm/hdmedi_server/global/config/jwt/Token.java new file mode 100644 index 0000000..b00cef3 --- /dev/null +++ b/src/main/java/com/kusithm/hdmedi_server/global/config/jwt/Token.java @@ -0,0 +1,16 @@ +package com.kusithm.hdmedi_server.global.config.jwt; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +public class Token { + private String accessToken; + private String refreshToken; + + public static Token of(String accessToken, String refreshToken) { + return new Token(accessToken, refreshToken); + } +} From a8d731986c917a52089356683a9b1b51746b7a88 Mon Sep 17 00:00:00 2001 From: RyuKwanKon Date: Wed, 6 Sep 2023 23:55:02 +0900 Subject: [PATCH 06/14] =?UTF-8?q?[feat]=20#5=20JWT=20Authentication=20Filt?= =?UTF-8?q?er=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/auth/JwtAuthenticationFilter.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/main/java/com/kusithm/hdmedi_server/global/config/auth/JwtAuthenticationFilter.java diff --git a/src/main/java/com/kusithm/hdmedi_server/global/config/auth/JwtAuthenticationFilter.java b/src/main/java/com/kusithm/hdmedi_server/global/config/auth/JwtAuthenticationFilter.java new file mode 100644 index 0000000..a8b42fa --- /dev/null +++ b/src/main/java/com/kusithm/hdmedi_server/global/config/auth/JwtAuthenticationFilter.java @@ -0,0 +1,47 @@ +package com.kusithm.hdmedi_server.global.config.auth; + +import com.kusithm.hdmedi_server.global.config.jwt.JwtProvider; +import com.kusithm.hdmedi_server.global.error.exception.UnauthorizedException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +import static com.kusithm.hdmedi_server.global.error.exception.ErrorCode.INVALID_ACCESS_TOKEN; + +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + private static final String AUTHORIZATION = "Authorization"; + private static final String BEARER = "Bearer "; + private final JwtProvider jwtProvider; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + final String accessToken = getAccessTokenFrom(request); + jwtProvider.validateAccessToken(accessToken); + final Long userId = jwtProvider.getSubject(accessToken); + setAuthentication(request, userId); + filterChain.doFilter(request, response); + } + + private String getAccessTokenFrom(HttpServletRequest request) { + String accessToken = request.getHeader(AUTHORIZATION); + if (StringUtils.hasText(accessToken) && accessToken.startsWith(BEARER)) + return accessToken.substring(BEARER.length()); + throw new UnauthorizedException(INVALID_ACCESS_TOKEN); + } + + private void setAuthentication(HttpServletRequest request, Long userId) { + UserAuthentication authentication = new UserAuthentication(userId, null, null); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + +} From 92d059c31ba76f8d86ec6cf690b0347a21f86a74 Mon Sep 17 00:00:00 2001 From: RyuKwanKon Date: Wed, 6 Sep 2023 23:55:49 +0900 Subject: [PATCH 07/14] =?UTF-8?q?[feat]=20#5=20AuthenticatedUserId=20?= =?UTF-8?q?=EC=95=A0=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/auth/AuthenticatedUserId.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/com/kusithm/hdmedi_server/global/config/auth/AuthenticatedUserId.java diff --git a/src/main/java/com/kusithm/hdmedi_server/global/config/auth/AuthenticatedUserId.java b/src/main/java/com/kusithm/hdmedi_server/global/config/auth/AuthenticatedUserId.java new file mode 100644 index 0000000..c983c42 --- /dev/null +++ b/src/main/java/com/kusithm/hdmedi_server/global/config/auth/AuthenticatedUserId.java @@ -0,0 +1,11 @@ +package com.kusithm.hdmedi_server.global.config.auth; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface AuthenticatedUserId { +} From 03438fa3bc85ef253a0502e4af777f827181d6e4 Mon Sep 17 00:00:00 2001 From: RyuKwanKon Date: Wed, 6 Sep 2023 23:56:32 +0900 Subject: [PATCH 08/14] =?UTF-8?q?[feat]=20#5=20Security=20resolver=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/auth/UserIdArgumentResolver.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/main/java/com/kusithm/hdmedi_server/global/config/auth/UserIdArgumentResolver.java diff --git a/src/main/java/com/kusithm/hdmedi_server/global/config/auth/UserIdArgumentResolver.java b/src/main/java/com/kusithm/hdmedi_server/global/config/auth/UserIdArgumentResolver.java new file mode 100644 index 0000000..5d45415 --- /dev/null +++ b/src/main/java/com/kusithm/hdmedi_server/global/config/auth/UserIdArgumentResolver.java @@ -0,0 +1,26 @@ +package com.kusithm.hdmedi_server.global.config.auth; + +import org.springframework.core.MethodParameter; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +@Component +public class UserIdArgumentResolver implements HandlerMethodArgumentResolver { + @Override + public boolean supportsParameter(MethodParameter parameter) { + boolean hasUserIdAnnotation = parameter.hasParameterAnnotation(AuthenticatedUserId.class); + boolean hasLongType = Long.class.isAssignableFrom(parameter.getParameterType()); + return hasUserIdAnnotation && hasLongType; + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + return SecurityContextHolder.getContext() + .getAuthentication() + .getPrincipal(); + } +} From 96c8423708a891fd111dbce434dc747c5aea9ae9 Mon Sep 17 00:00:00 2001 From: RyuKwanKon Date: Wed, 6 Sep 2023 23:56:47 +0900 Subject: [PATCH 09/14] =?UTF-8?q?[feat]=20#5=20resolver=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/WebConfig.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/com/kusithm/hdmedi_server/global/config/WebConfig.java diff --git a/src/main/java/com/kusithm/hdmedi_server/global/config/WebConfig.java b/src/main/java/com/kusithm/hdmedi_server/global/config/WebConfig.java new file mode 100644 index 0000000..df6e2cf --- /dev/null +++ b/src/main/java/com/kusithm/hdmedi_server/global/config/WebConfig.java @@ -0,0 +1,21 @@ +package com.kusithm.hdmedi_server.global.config; + +import com.kusithm.hdmedi_server.global.config.auth.UserIdArgumentResolver; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@RequiredArgsConstructor +@Configuration +public class WebConfig implements WebMvcConfigurer { + private final UserIdArgumentResolver userIdArgumentResolver; + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(userIdArgumentResolver); + } +} + From ac9c5dc553fb7cdb6a5466d200545b4cb7171ae0 Mon Sep 17 00:00:00 2001 From: RyuKwanKon Date: Wed, 6 Sep 2023 23:57:14 +0900 Subject: [PATCH 10/14] =?UTF-8?q?[feat]=20#5=20User=20Authentication=20?= =?UTF-8?q?=EC=B6=94=EC=83=81=ED=99=94=20=EA=B0=9D=EC=B2=B4=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/auth/UserAuthentication.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/com/kusithm/hdmedi_server/global/config/auth/UserAuthentication.java diff --git a/src/main/java/com/kusithm/hdmedi_server/global/config/auth/UserAuthentication.java b/src/main/java/com/kusithm/hdmedi_server/global/config/auth/UserAuthentication.java new file mode 100644 index 0000000..6eca56a --- /dev/null +++ b/src/main/java/com/kusithm/hdmedi_server/global/config/auth/UserAuthentication.java @@ -0,0 +1,12 @@ +package com.kusithm.hdmedi_server.global.config.auth; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +public class UserAuthentication extends UsernamePasswordAuthenticationToken { + public UserAuthentication(Object principal, Object credentials, Collection authorities) { + super(principal, credentials, authorities); + } +} From f86b7f9221e39f4b0ca652a9ff44e27e84c542f6 Mon Sep 17 00:00:00 2001 From: RyuKwanKon Date: Wed, 6 Sep 2023 23:57:47 +0900 Subject: [PATCH 11/14] =?UTF-8?q?[feat]=20#5=20JWT=20=EC=9D=B8=EC=A6=9D=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=EC=8B=9C=20=EB=8F=99=EC=9E=91=ED=95=98?= =?UTF-8?q?=EB=8A=94=20Entry=20Point=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/JwtAuthenticationEntryPoint.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/main/java/com/kusithm/hdmedi_server/global/config/auth/JwtAuthenticationEntryPoint.java diff --git a/src/main/java/com/kusithm/hdmedi_server/global/config/auth/JwtAuthenticationEntryPoint.java b/src/main/java/com/kusithm/hdmedi_server/global/config/auth/JwtAuthenticationEntryPoint.java new file mode 100644 index 0000000..97a2e86 --- /dev/null +++ b/src/main/java/com/kusithm/hdmedi_server/global/config/auth/JwtAuthenticationEntryPoint.java @@ -0,0 +1,34 @@ +package com.kusithm.hdmedi_server.global.config.auth; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.kusithm.hdmedi_server.global.error.dto.ErrorBaseResponse; +import com.kusithm.hdmedi_server.global.error.exception.ErrorCode; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.MediaType; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * 인증되지 않은 유저가 요청을 했을 때 동작하는 객체. + * 예외를 다루기 위해 사용합니다. + */ +@Component +public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { + handleException(response); + } + + private void handleException(HttpServletResponse response) throws IOException { + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding("utf-8"); + response.setStatus(ErrorCode.UNAUTHORIZED.getHttpStatus().value()); + response.getWriter().write(objectMapper.writeValueAsString(ErrorBaseResponse.of(ErrorCode.UNAUTHORIZED))); + } +} From e028671cc0becb92d347465c6d617644264b2eee Mon Sep 17 00:00:00 2001 From: RyuKwanKon Date: Wed, 6 Sep 2023 23:58:11 +0900 Subject: [PATCH 12/14] =?UTF-8?q?[feat]=20#5=20Security=20Filter=20Excepti?= =?UTF-8?q?on=20Handler=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/auth/ExceptionHandlerFilter.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/main/java/com/kusithm/hdmedi_server/global/config/auth/ExceptionHandlerFilter.java diff --git a/src/main/java/com/kusithm/hdmedi_server/global/config/auth/ExceptionHandlerFilter.java b/src/main/java/com/kusithm/hdmedi_server/global/config/auth/ExceptionHandlerFilter.java new file mode 100644 index 0000000..1a520f3 --- /dev/null +++ b/src/main/java/com/kusithm/hdmedi_server/global/config/auth/ExceptionHandlerFilter.java @@ -0,0 +1,55 @@ +package com.kusithm.hdmedi_server.global.config.auth; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.kusithm.hdmedi_server.global.error.dto.ErrorBaseResponse; +import com.kusithm.hdmedi_server.global.error.exception.ErrorCode; +import com.kusithm.hdmedi_server.global.error.exception.InvalidValueException; +import com.kusithm.hdmedi_server.global.error.exception.UnauthorizedException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +/** + * Filter 단계에서 발생할 수 있는 예외를 처리하는 객체입니다. + * JwtAuthenticationFilter에서 발생할 수 있는 예외를 관리하는 객체라고 생각해주시면 될 것 같습니다. + * SecurityConfig에서 JwtAuthenticationFilter를 등록 후 해당하는 예외를 관리하기 위해 등록합니다. + */ +@Slf4j +public class ExceptionHandlerFilter extends OncePerRequestFilter { + private final ObjectMapper objectMapper = new ObjectMapper(); + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + try { + filterChain.doFilter(request, response); + } catch (UnauthorizedException e) { + handleUnauthorizedException(response, e); + } catch (Exception ee) { + handleException(response); + } + } + + private void handleUnauthorizedException(HttpServletResponse response, Exception e) throws IOException { + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding("utf-8"); + if (e instanceof UnauthorizedException ue) { + response.setStatus(ue.getErrorCode().getHttpStatus().value()); + response.getWriter().write(objectMapper.writeValueAsString(ErrorBaseResponse.of(ue.getErrorCode()))); + } else if (e instanceof InvalidValueException ie) { + response.setStatus(ie.getErrorCode().getHttpStatus().value()); + response.getWriter().write(objectMapper.writeValueAsString(ErrorBaseResponse.of(ie.getErrorCode()))); + } + } + + private void handleException(HttpServletResponse response) throws IOException { + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding("utf-8"); + response.setStatus(ErrorCode.INTERNAL_SERVER_ERROR.getHttpStatus().value()); + response.getWriter().write(objectMapper.writeValueAsString(ErrorBaseResponse.of(ErrorCode.INTERNAL_SERVER_ERROR))); + } +} From b9829736fb5894afa3d3320ba16a9637e6088359 Mon Sep 17 00:00:00 2001 From: RyuKwanKon Date: Wed, 6 Sep 2023 23:58:30 +0900 Subject: [PATCH 13/14] =?UTF-8?q?[feat]=20#5=20Security=20config=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/auth/SecurityConfig.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/main/java/com/kusithm/hdmedi_server/global/config/auth/SecurityConfig.java diff --git a/src/main/java/com/kusithm/hdmedi_server/global/config/auth/SecurityConfig.java b/src/main/java/com/kusithm/hdmedi_server/global/config/auth/SecurityConfig.java new file mode 100644 index 0000000..f711258 --- /dev/null +++ b/src/main/java/com/kusithm/hdmedi_server/global/config/auth/SecurityConfig.java @@ -0,0 +1,47 @@ +package com.kusithm.hdmedi_server.global.config.auth; + +import com.kusithm.hdmedi_server.global.config.jwt.JwtProvider; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@RequiredArgsConstructor +@EnableWebSecurity +@Configuration +public class SecurityConfig { + + private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + private final JwtProvider jwtProvider; + // TODO api 추가될 때 white list url 확인해서 추가하기. + private static final String[] whiteList = {"/api/user/signin", "/api/user/signup", "/api/user/reissue", "/"}; + + @Bean + public WebSecurityCustomizer webSecurityCustomizer() { + return web -> web.ignoring().requestMatchers(whiteList); + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + return http + .formLogin(AbstractHttpConfigurer::disable) + .httpBasic(AbstractHttpConfigurer::disable) + .csrf(AbstractHttpConfigurer::disable) + .sessionManagement(sessionManagementConfigurer -> + sessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .exceptionHandling(exceptionHandlingConfigurer -> + exceptionHandlingConfigurer.authenticationEntryPoint(jwtAuthenticationEntryPoint)) + .authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> + authorizationManagerRequestMatcherRegistry.anyRequest().authenticated()) + .addFilterBefore(new JwtAuthenticationFilter(jwtProvider), UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(new ExceptionHandlerFilter(), JwtAuthenticationFilter.class) + .build(); + } + +} From d18012181ca9a1d390cdc617f7dfae04683e7897 Mon Sep 17 00:00:00 2001 From: RyuKwanKon Date: Wed, 6 Sep 2023 23:58:41 +0900 Subject: [PATCH 14/14] =?UTF-8?q?[feat]=20#5=20JPA=20Config=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kusithm/hdmedi_server/global/config/JpaConfig.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/com/kusithm/hdmedi_server/global/config/JpaConfig.java diff --git a/src/main/java/com/kusithm/hdmedi_server/global/config/JpaConfig.java b/src/main/java/com/kusithm/hdmedi_server/global/config/JpaConfig.java new file mode 100644 index 0000000..91183a6 --- /dev/null +++ b/src/main/java/com/kusithm/hdmedi_server/global/config/JpaConfig.java @@ -0,0 +1,9 @@ +package com.kusithm.hdmedi_server.global.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@EnableJpaAuditing +@Configuration +public class JpaConfig { +}