-
Notifications
You must be signed in to change notification settings - Fork 2
[FEAT] 소셜 로그인 API및 Security, JWT #41
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
Merged
Merged
Changes from all commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
483f11b
feat: 시큐리티 메인, getCurrentUserId() 유틸 구현 (#25)
kimyw1018 cf73e51
feat: 시큐리티 전용 userdetails 및 service 구현 (#25)
kimyw1018 60fb0e8
feat: jwt 토큰 생성 및 유효성 검증 유틸리티 구현 (#25)
kimyw1018 7c67b37
feat: http 요청 토큰 검증을 위한 jwt 인증 필터 구현 (#25)
kimyw1018 e1e71c2
feat: hjwt 인증 실패 및 권한 부족 예외 핸들러 구현 (#25)
kimyw1018 346270b
feat: securityconfig 내 jwt 필터 및 예외 핸들러 체인 등록 (#25)
kimyw1018 17e5f51
feat: 소셜 로그인 api 및 비즈니스 로직 뼈대 구현(#25)
kimyw1018 ff23e22
Merge branch 'develop' of https://github.com/Musereview/BE into feat/…
kimyw1018 e48ba51
feat: 코드 스타일 정리 (#25)
kimyw1018 bd65765
feat: CommonStatus 내 UNAUTHORIZED, FORBIDDEN 상수 추가 (#25)
kimyw1018 7ba95bb
feat: 예외 코드 변경 (#25)
kimyw1018 da3a472
feat: 액세스, 리프레시 토근 구분 (#25)
kimyw1018 0b6a6bd
feat: 액세스토큰 검증 함수 명확히 (#25)
kimyw1018 4926638
feat: email속성 제거 (#25)
kimyw1018 a636bb4
feat: 유저 엔티티 연결 (#25)
kimyw1018 6613435
feat: 주석 처리 (#25)
kimyw1018 879730a
feat: 다음 작업 분리 (#25)
kimyw1018 3bfaaaa
feat: Base64 기준으로 수정 (#25)
kimyw1018 809047b
feat: 소셜 로그인 구현 전 방어 (#25)
kimyw1018 1ffca71
feat: jwt 키 형식 체크 (#25)
kimyw1018 6119db5
feat: 500대신 401처리로 감싸기 (#25)
kimyw1018 bc04e7c
feat: 유저 역할 임의 부여 제거 (#25)
kimyw1018 678eb4e
feat: 이메일 속성 삭제 (#25)
kimyw1018 0ed59e6
feat: 타입 캐스팅 로직 수정 (#25)
kimyw1018 76ee530
feat: 토큰 유효성 private, jwt프로퍼티 반영 , 예외 처리(#25)
kimyw1018 8120ae7
feat: resolveToken 하드코딩 제거 (#25)
kimyw1018 f19f57c
feat: 앤드포인트 컨밴션 (#25)
kimyw1018 8c45bc1
feat: 비인증 허용 api 수정 (#25)
kimyw1018 5b69e50
feat: cors 추가 (#25)
kimyw1018 65967dc
feat: ObjectMapper주입 (#25)
kimyw1018 c0e7b86
feat: 유저 role 이넘 생성 (#25)
kimyw1018 f609452
feat: 유저 role 이넘관련 로직 추가 (#25)
kimyw1018 3754aa3
feat: jwt 토큰 응답 세분화 (#25)
kimyw1018 bb295aa
feat: 네이밍, 타입 수정(#25)
kimyw1018 4530b88
feat: 네이밍수정(#25)
kimyw1018 d8a49eb
Merge: 최신 상태 반영 (#25)
kimyw1018 1ab1d2a
feat: 와일드카드, 위험성 제거 (#25)
kimyw1018 1b90a4d
feat: 주석 정리(#25)
kimyw1018 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
src/main/java/com/mr/domain/auth/controller/AuthController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package com.mr.domain.auth.controller; | ||
|
|
||
| import com.mr.domain.auth.dto.AuthRequestDTO; | ||
| import com.mr.domain.auth.dto.AuthResponseDTO; | ||
| import com.mr.domain.auth.entity.enums.SocialType; | ||
| import com.mr.domain.auth.service.AuthService; | ||
| import com.mr.global.apipayload.ApiResponse; | ||
| import jakarta.validation.Valid; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.context.annotation.Profile; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
| import org.springframework.web.bind.annotation.PostMapping; | ||
| import org.springframework.web.bind.annotation.RequestBody; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @RestController | ||
| @RequiredArgsConstructor | ||
| @RequestMapping("/api/auth") | ||
| @Profile({"local", "dev"}) | ||
| public class AuthController { | ||
|
|
||
| private final AuthService authService; | ||
|
|
||
| @PostMapping("/login/{socialType}") | ||
| public ApiResponse<AuthResponseDTO.LoginResponse> socialLogin( | ||
| @PathVariable(name = "socialType") SocialType socialType, | ||
|
kimyw1018 marked this conversation as resolved.
|
||
| @RequestBody @Valid AuthRequestDTO.SocialLoginRequest request | ||
| ) { | ||
| AuthResponseDTO.LoginResponse response = authService.socialLogin(socialType, request.accessToken()); | ||
| return ApiResponse.onSuccess(response); | ||
| } | ||
|
kimyw1018 marked this conversation as resolved.
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package com.mr.domain.auth.dto; | ||
|
|
||
| import jakarta.validation.constraints.NotBlank; | ||
|
|
||
| public class AuthRequestDTO { | ||
|
|
||
| public record SocialLoginRequest( | ||
| @NotBlank(message = "소셜 액세스 토큰은 필수 입력값입니다.") | ||
| String accessToken | ||
| ) {} | ||
|
|
||
| public record TokenRefreshRequest( | ||
| @NotBlank(message = "Refresh Token은 필수 입력값입니다.") | ||
| String refreshToken | ||
| ) {} | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package com.mr.domain.auth.dto; | ||
|
|
||
| import lombok.Builder; | ||
|
|
||
| public class AuthResponseDTO { | ||
|
|
||
| @Builder | ||
| public record TokenResponse( | ||
| String accessToken, | ||
| String refreshToken, | ||
| Long accessTokenExpiresInSeconds | ||
| ) {} | ||
|
|
||
| @Builder | ||
| public record LoginResponse( | ||
| Long userId, | ||
| String nickname, | ||
| boolean isNewUser, | ||
| TokenResponse tokenInfo | ||
| ) {} | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 2 additions & 2 deletions
4
src/main/java/com/mr/domain/auth/entity/enums/SocialType.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| package com.mr.domain.auth.entity.enums; | ||
|
|
||
| public enum SocialType { | ||
| KAKAO, | ||
| kakao, | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| package com.mr.domain.auth.service; | ||
|
|
||
| import com.mr.domain.auth.dto.AuthResponseDTO; | ||
| import com.mr.domain.auth.entity.enums.SocialType; | ||
| import com.mr.global.security.jwt.JwtTokenProvider; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| @Service | ||
| @RequiredArgsConstructor | ||
| @Transactional(readOnly = true) | ||
| public class AuthService { | ||
|
|
||
| private final JwtTokenProvider jwtTokenProvider; | ||
| // private final KakaoOAuthService kakaoOAuthService; (외부 API 파싱용 서비스) | ||
| // private final GoogleOAuthService googleOAuthService; | ||
|
|
||
| @Transactional | ||
| public AuthResponseDTO.LoginResponse socialLogin(SocialType socialType, String accessToken) { | ||
| // 1. 외부 소셜 API (카카오/구글) 통신하여 유저 프로필(email, socialId) 파싱 | ||
| // SocialUserInfo userInfo = getSocialUserInfo(socialType, accessToken); | ||
|
|
||
| // 2. TODO: User 엔티티 연동 및 가입여부 검증 (Stub 구조) | ||
| // 만약 가입 안 되어있으면 DB User 생성 -> 저장 | ||
| Long mockUserId = 1L; | ||
| String mockEmail = "user@example.com"; | ||
| String mockNickname = "뮤즈유저"; | ||
| boolean isNewUser = false; | ||
|
|
||
| String appAccessToken = jwtTokenProvider.createAccessToken(mockUserId); | ||
| String appRefreshToken = jwtTokenProvider.createRefreshToken(mockUserId); | ||
|
|
||
|
|
||
| AuthResponseDTO.TokenResponse tokenResponse = AuthResponseDTO.TokenResponse.builder() | ||
| .accessToken(appAccessToken) | ||
| .refreshToken(appRefreshToken) | ||
| .accessTokenExpiresInSeconds(3600L) | ||
| .build(); | ||
| return AuthResponseDTO.LoginResponse.builder() | ||
| .userId(mockUserId) | ||
| .nickname(mockNickname) | ||
| .isNewUser(isNewUser) | ||
| .tokenInfo(tokenResponse) | ||
| .build(); | ||
| } | ||
| } |
15 changes: 15 additions & 0 deletions
15
src/main/java/com/mr/domain/user/entity/enums/UserRole.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package com.mr.domain.user.entity.enums; | ||
|
|
||
| import lombok.Getter; | ||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| @Getter | ||
| @RequiredArgsConstructor | ||
| public enum UserRole { | ||
| ROLE_STUDENT("ROLE_STUDENT", "학생"), | ||
| ROLE_TEACHER("ROLE_TEACHER", "강사"), | ||
| ROLE_ADMIN("ROLE_ADMIN", "관리자"); | ||
|
|
||
| private final String key; | ||
| private final String title; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| package com.mr.global.config; | ||
|
|
||
| public class SwaggerConfig { | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| package com.mr.global.security; | ||
|
|
||
| import com.mr.global.security.jwt.JwtAccessDeniedHandler; | ||
| import com.mr.global.security.jwt.JwtAuthenticationEntryPoint; | ||
| import com.mr.global.security.jwt.JwtAuthenticationFilter; | ||
| import com.mr.global.security.jwt.JwtTokenProvider; | ||
| 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.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.CorsConfiguration; | ||
| import org.springframework.web.cors.CorsConfigurationSource; | ||
| import org.springframework.web.cors.UrlBasedCorsConfigurationSource; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @Configuration | ||
| @EnableWebSecurity | ||
| @RequiredArgsConstructor | ||
| public class SecurityConfig { | ||
|
|
||
| private final JwtTokenProvider jwtTokenProvider; | ||
| private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; | ||
| private final JwtAccessDeniedHandler jwtAccessDeniedHandler; | ||
|
|
||
| private static final String[] PUBLIC_URLS = { | ||
| "/swagger-ui/**", | ||
| "/v3/api-docs/**", | ||
| "/api/auth/login/**", | ||
| "/api/auth/refactor" | ||
| }; | ||
|
|
||
| @Bean | ||
|
kimyw1018 marked this conversation as resolved.
|
||
| public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { | ||
| http | ||
| .cors(cors -> cors.configurationSource(corsConfigurationSource())) | ||
| .csrf(AbstractHttpConfigurer::disable) | ||
| .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) | ||
| .exceptionHandling(exception -> exception | ||
| .authenticationEntryPoint(jwtAuthenticationEntryPoint) | ||
| .accessDeniedHandler(jwtAccessDeniedHandler) | ||
| ) | ||
| .authorizeHttpRequests(auth -> auth | ||
| .requestMatchers(PUBLIC_URLS).permitAll() | ||
| .anyRequest().authenticated() | ||
| ) | ||
| .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); | ||
|
|
||
| return http.build(); | ||
| } | ||
|
|
||
| // CORS 설정 | ||
| @Bean | ||
| public CorsConfigurationSource corsConfigurationSource() { | ||
| CorsConfiguration configuration = new CorsConfiguration(); | ||
|
|
||
| configuration.setAllowedOriginPatterns(List.of( | ||
| "http://localhost:3000", | ||
| "http://localhost:5173", | ||
| "https://*.musereview.site" | ||
| )); | ||
|
|
||
| configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); | ||
|
|
||
| configuration.setAllowedHeaders(List.of("Authorization", "Content-Type", "X-Requested-With")); | ||
|
|
||
| configuration.setExposedHeaders(List.of("Authorization")); | ||
|
|
||
| configuration.setAllowCredentials(true); | ||
|
|
||
| configuration.setMaxAge(3600L); | ||
|
|
||
| UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); | ||
| source.registerCorsConfiguration("/**", configuration); | ||
| return source; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package com.mr.global.security; | ||
|
|
||
| import com.mr.global.apipayload.code.CommonStatus; | ||
| import com.mr.global.apipayload.exception.GeneralException; | ||
| import com.mr.global.security.principal.CustomUserDetails; | ||
| import org.springframework.security.authentication.AnonymousAuthenticationToken; | ||
| import org.springframework.security.core.Authentication; | ||
| import org.springframework.security.core.context.SecurityContextHolder; | ||
|
|
||
| public class SecurityUtil { | ||
|
|
||
| private SecurityUtil() { | ||
| } | ||
|
|
||
| public static Long getCurrentUserId() { | ||
| final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); | ||
|
|
||
| if (authentication == null | ||
| || !authentication.isAuthenticated() | ||
| || authentication instanceof AnonymousAuthenticationToken) { | ||
| throw new GeneralException(CommonStatus.UNAUTHORIZED); | ||
|
Contributor
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. 인증 정보가 없을 때 유입될 수 있는 anonymousUser 상황까지 놓치지 않고 꼼꼼하게 예외 처리해 주셨네요. |
||
| } | ||
|
|
||
| if (authentication.getPrincipal() instanceof CustomUserDetails userDetails) { | ||
| return userDetails.getUserId(); | ||
| } | ||
|
|
||
| throw new GeneralException(CommonStatus.UNAUTHORIZED); | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.