-
Notifications
You must be signed in to change notification settings - Fork 0
#10 카카오로그인구현 #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The head ref may contain hidden characters: "#10-\uCE74\uCE74\uC624\uB85C\uADF8\uC778\uAD6C\uD604"
#10 카카오로그인구현 #11
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,24 @@ | ||
| package com.example.gtable.global.api; | ||
|
|
||
| import lombok.Getter; | ||
| import org.springframework.http.HttpStatus; | ||
|
|
||
| import lombok.Getter; | ||
|
|
||
| @Getter | ||
| public class ApiError { | ||
| private final String message; | ||
| private final int status; | ||
| private final String message; | ||
| private final int status; | ||
|
|
||
| public ApiError(String message, int status) { | ||
| this.message = message; | ||
| this.status = status; | ||
| } | ||
| public ApiError(String message, int status) { | ||
| this.message = message; | ||
| this.status = status; | ||
| } | ||
|
|
||
| public ApiError(Throwable throwable, HttpStatus status) { | ||
| this(throwable.getMessage(), status); | ||
| } | ||
| public ApiError(Throwable throwable, HttpStatus status) { | ||
| this(throwable.getMessage(), status); | ||
| } | ||
|
|
||
| public ApiError(String message, HttpStatus status) { | ||
| this(message, status.value()); | ||
| } | ||
| public ApiError(String message, HttpStatus status) { | ||
| this(message, status.value()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,21 +1,21 @@ | ||
| package com.example.gtable.global.api; | ||
|
|
||
| public class ApiResult<T> { | ||
| private final boolean success; | ||
| private final T response; | ||
| private final ApiError error; | ||
| private final boolean success; | ||
| private final T response; | ||
| private final ApiError error; | ||
|
|
||
| public ApiResult(boolean success, T response, ApiError error) { | ||
| this.success = success; | ||
| this.response = response; | ||
| this.error = error; | ||
| } | ||
| public ApiResult(boolean success, T response, ApiError error) { | ||
| this.success = success; | ||
| this.response = response; | ||
| this.error = error; | ||
| } | ||
|
|
||
| public boolean isSuccess() { | ||
| return success; | ||
| } | ||
| public boolean isSuccess() { | ||
| return success; | ||
| } | ||
|
|
||
| public T getResponse() { | ||
| return response; | ||
| } | ||
| public T getResponse() { | ||
| return response; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,21 @@ | ||
| package com.example.gtable.global.api; | ||
|
|
||
| import org.springframework.http.HttpStatus; | ||
|
|
||
| import lombok.AccessLevel; | ||
| import lombok.NoArgsConstructor; | ||
| import org.springframework.http.HttpStatus; | ||
|
|
||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| public class ApiUtils { | ||
| public static <T> ApiResult<T> success(T response) { | ||
| return new ApiResult<>(true, response,null); | ||
| } | ||
| public static <T> ApiResult<T> success(T response) { | ||
| return new ApiResult<>(true, response, null); | ||
| } | ||
|
|
||
| public static ApiResult<?> error(Throwable throwable, HttpStatus status) { | ||
| return new ApiResult<>(false, null, new ApiError(throwable, status)); | ||
| } | ||
| public static ApiResult<?> error(Throwable throwable, HttpStatus status) { | ||
| return new ApiResult<>(false, null, new ApiError(throwable, status)); | ||
| } | ||
|
|
||
| public static ApiResult<?> error(String message,HttpStatus status) { | ||
| return new ApiResult<>(false, null, new ApiError(message,status)); | ||
| } | ||
| public static ApiResult<?> error(String message, HttpStatus status) { | ||
| return new ApiResult<>(false, null, new ApiError(message, status)); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package com.example.gtable.global.config; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.web.cors.CorsConfiguration; | ||
| import org.springframework.web.cors.CorsConfigurationSource; | ||
| import org.springframework.web.cors.UrlBasedCorsConfigurationSource; | ||
|
|
||
| @Configuration | ||
| public class CorsConfig { | ||
| @Bean | ||
| public CorsConfigurationSource corsConfigurationSource() { | ||
| CorsConfiguration config = new CorsConfiguration(); | ||
|
|
||
| config.setAllowCredentials(true); // 쿠키나 인증헤더 자격증명 허용 | ||
| config.setAllowedOrigins(List.of("http://localhost:3000")); // 허용할 출처 설정 | ||
| config.setAllowedMethods(List.of("GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS")); // 메서드 허용 | ||
| config.setAllowedHeaders(List.of("*")); //클라이언트가 보낼 수 있는 헤더 | ||
| config.setExposedHeaders(List.of("Authorization")); //클라이언트(브라우저)가 접근할 수 있는 헤더 지정 | ||
| // config.setAllowCredentials(true); // 쿠키 포함 허용 | ||
|
|
||
| UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); | ||
| source.registerCorsConfiguration("/**", config); //** 뜻은 모든 URL 경로에 적용한다는 의미 | ||
| return source; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package com.example.gtable.global.config; | ||
|
|
||
| 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.configurers.AbstractHttpConfigurer; | ||
| import org.springframework.security.config.http.SessionCreationPolicy; | ||
| import org.springframework.security.web.SecurityFilterChain; | ||
| import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
| import org.springframework.web.cors.CorsConfigurationSource; | ||
|
|
||
| import com.example.gtable.global.security.jwt.JwtAuthorizationFilter; | ||
| import com.example.gtable.global.security.jwt.JwtUtil; | ||
| import com.example.gtable.global.security.oauth2.CustomOAuth2UserService; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| @Configuration | ||
| @EnableWebSecurity // security 활성화 어노테이션 | ||
| @RequiredArgsConstructor | ||
| public class SecurityConfig { | ||
| private final CustomOAuth2UserService customOAuth2UserService; | ||
| private final com.example.gtable.global.security.oauth2.OAuth2LoginSuccessHandler OAuth2LoginSuccessHandler; | ||
| private final JwtUtil jwtUtil; | ||
|
|
||
| private final CorsConfigurationSource corsConfigurationSource; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion CORS 설정이 주입되었지만 사용되지 않고 있습니다
다음과 같이 CORS 설정을 추가해주세요: http
+ // CORS 설정 적용
+ .cors(cors -> cors.configurationSource(corsConfigurationSource))
// CSRF 방어 기능 비활성화 (jwt 토큰을 사용할 것이기에 필요없음)
.csrf(AbstractHttpConfigurer::disable)🤖 Prompt for AI Agents |
||
|
|
||
| @Bean | ||
| public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { | ||
| http | ||
| // CSRF 방어 기능 비활성화 (jwt 토큰을 사용할 것이기에 필요없음) | ||
| .csrf(AbstractHttpConfigurer::disable) | ||
| // 시큐리티 폼 로그인 비활성화 | ||
| .formLogin(AbstractHttpConfigurer::disable) | ||
| // HTTP Basic 인증 비활성화 | ||
| .httpBasic(AbstractHttpConfigurer::disable) | ||
| // oauth2 로그인 | ||
| // - userInfoEndPoint에서 사용자 정보 불러오고, | ||
| // - successHandler에서 로그인 성공 시 JWT 생성 및 반환로직 | ||
| .oauth2Login(oauth2 -> | ||
| oauth2.userInfoEndpoint(userInfoEndpoint -> | ||
| userInfoEndpoint.userService(customOAuth2UserService) | ||
| ).successHandler(OAuth2LoginSuccessHandler) | ||
| ) | ||
| // 세션 사용하지 않음 | ||
| .sessionManagement(session -> | ||
| session.sessionCreationPolicy(SessionCreationPolicy.STATELESS) | ||
| ) | ||
| .authorizeHttpRequests(auth -> auth | ||
| .requestMatchers( | ||
| "/oauth2/authorization/kakao", // 카카오 로그인 요청 | ||
| "/login/oauth2/code/**", // 카카오 인증 콜백 | ||
| "/api/refresh-token") // refresh token (토큰 갱신) | ||
| .permitAll() | ||
| .anyRequest().authenticated() // 그외 요청은 허가된 사람만 인가 | ||
| ) | ||
| // JWTFiler | ||
| .addFilterBefore(new JwtAuthorizationFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class); | ||
|
|
||
| return http.build(); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package com.example.gtable.global.security.exception; | ||
|
|
||
| public abstract class BusinessException extends RuntimeException { | ||
| private final ErrorMessage errorMessage; | ||
|
|
||
| protected BusinessException(ErrorMessage errorMessage) { | ||
| super(errorMessage.getMessage()); | ||
| this.errorMessage = errorMessage; | ||
| } | ||
|
|
||
| public String getCode() { | ||
| return errorMessage.getCode(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package com.example.gtable.global.security.exception; | ||
|
|
||
| import lombok.Getter; | ||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| @Getter | ||
| @RequiredArgsConstructor | ||
| public enum ErrorMessage { | ||
| // global | ||
| INVALID_INPUT_VALUE("입력값이 올바르지 않습니다.", "g001"), | ||
|
|
||
| // auth | ||
| UNAUTHORIZED("권한이 없습니다", "a001"), | ||
|
|
||
| // token | ||
| REFRESH_TOKEN_NOT_FOUND("기존 리프레시 토큰을 찾을 수 없습니다.", "t001"), | ||
| DOES_NOT_MATCH_REFRESH_TOKEN("기존 리프레시 토큰이 일치하지 않습니다.", "t002"); | ||
|
|
||
| private final String message; | ||
| private final String code; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package com.example.gtable.global.security.exception; | ||
|
|
||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
|
|
||
| import lombok.Getter; | ||
|
|
||
| @Getter | ||
| public class ErrorResponse { | ||
| private final String message; | ||
| private final String code; | ||
| private final Map<String, String> errors; | ||
|
|
||
| public ErrorResponse(String message, String code) { | ||
| this.message = message; | ||
| this.code = code; | ||
| errors = new HashMap<>(); | ||
| } | ||
|
|
||
| public ErrorResponse(String message, String code, Map<String, String> errors) { | ||
| this.message = message; | ||
| this.code = code; | ||
| this.errors = errors; | ||
| } | ||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
CORS 설정이 잘 구성되어 있지만 몇 가지 개선점이 있습니다
전반적으로 CORS 설정이 적절히 구성되어 있네요. 다만 몇 가지 개선할 점들이 있습니다:
@Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); // 쿠키나 인증헤더 자격증명 허용 config.setAllowedOrigins(List.of("http://localhost:3000")); // 허용할 출처 설정 config.setAllowedMethods(List.of("GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS")); // 메서드 허용 config.setAllowedHeaders(List.of("*")); //클라이언트가 보낼 수 있는 헤더 config.setExposedHeaders(List.of("Authorization")); //클라이언트(브라우저)가 접근할 수 있는 헤더 지정 - // config.setAllowCredentials(true); // 쿠키 포함 허용 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); //** 뜻은 모든 URL 경로에 적용한다는 의미 return source; }운영 환경을 고려한다면 다음과 같은 개선도 고려해보시면 좋겠어요:
🤖 Prompt for AI Agents