-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Feat: Security가 적용된 이후 기본적으로 api 실행이 가능한 상태를 유지하기 위해, 우선은 apiEndpoint를 permitAll로 설정 * Feat: 헤더로부터 Jwt를 추출하고 파싱하여 인증을 하기위한 필터 구성 * Feat: 사용자의 권한 관리를 위해 user_roles 추가 * Fix: nullpointException 관련 문제로 필터 작동 및 수정된 포인트 결제가 제대로 수행되지 않던 문제 수정 * Feat: Jwt 필터 처리중에 발생한 에러를 처리하기위한 JwtExceptionFilter 구현 * Feat: Jwt 인증필터와 SpringSecurity의 충돌을 피하기위해, Jwt 인증 필터와 예외 필터, securityFilterChain에 추가 * Feat: 필터를 순회하는 도중에 발생하는 인가 관련 에러를 잡아내기 위한 AccessDeniedHandler, AuthenticationEntryPoint 구현 * Chore: 시스템 환경에 따라 유연하게 그리고 민감한 정보를 public repository에 드러내지 않게 하기위해 환경변수 파일로 대체 * Fix: 트래킹이 안되고 있는 파일 수정 * Feat: oauth2 시큐리티 설정 추가 * Feat: 상이해질 수 있는 서비스 제공자의 사용자 모델의 일관된 표준을 지정하기위해, ProviderUser 모델 구현 * Feat: ProviderUser를 기준으로 Naver 로그인과 Google OAuth2에 호환되는 사용자 데이터 모델링 구성 * Refactor: 소셜 로그인 중 신규 회원가입을 수행하기 위해 기존 User 엔티티에 email 추가 * Feat: OAuth2 인가 서비스를 사용할 Service 클래스 구성 1. 소셜 로그인 이후, OAuth2UserRequest를 통해 인가 서버로부터 받아온 AccessToken 및 OAuth2 서비즈 제공자 식별 데이터 및 attribute 수신 2. providerConverter를 통해, OAuth2 서비스 제공자에 맞는 ProviderUser 생성 3. ProviderUser 조회 후, 서버에 등록된 사용자가 아니면 사용자 등록 진행 4. Security의 인증/인가 여부를 판단하기위한 PrincipalUser 객체 리턴 * Feat: 다양한 OAuth2 서비스 제공자를 추가하여도 기존 코드를 변경하지 않게함으로서 확장성을 높이기위해, 전략 패턴 및 책임 연쇄 패턴 활용 * Chore: OAuth2 시연을 위해 타임리프 템플릿 의존성 추가 * Feat: 소셜 로그인 시연용 html 렌더링을 위해, 기존에 시큐리티가 막고있는 정적 리소스의 접근들 중, 화면 렌더링에 필요한 자원들만 허용 * Refactor: 스프링 시큐리티의 권장사항으로서, 정적 자원 접근에 대한 관리는 HttpSecurity 필터체인에게 위임하도록 한다. * Feat: 최초 소셜 로그인시 계정 등록에 필요한 orm 새로 구성 * Refactor: 사용자 등록 혹은 로그인에 필요한 컬럼 스키마에 반영 * Feat: OAuth2 소셜 로그인 시연에 필요한 컨트롤러 및 웰컴 페이지 구현 * Feat: 소셜 로그인으로 받은 인가 정보를 매핑하기 위한 CustomAuthorityMapper 구현 및 적용 * Feat: 기본 oauth2 로그인 페이지를 시연용으로 제작한 로그인 페이지로 전환시키기 위해, LoginController와 SecurityConfig 설정 적용 * Fix: OAuth2 소셜 로그인 시연하던 중에 발생한 null 관련 오류 수정 * Feat: kakao 소셜 로그인 추가
- Loading branch information
Showing
58 changed files
with
4,445 additions
and
31 deletions.
There are no files selected for viewing
This file contains 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 |
---|---|---|
|
@@ -46,4 +46,6 @@ out/ | |
.kotlin | ||
|
||
### log ### | ||
logs/ | ||
logs/ | ||
|
||
/src/main/docker/.env |
This file contains 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 contains 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
25 changes: 25 additions & 0 deletions
25
...main/kotlin/io/ticketaka/api/common/infrastructure/security/DefaultAccessDeniedHandler.kt
This file contains 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,25 @@ | ||
package io.ticketaka.api.common.infrastructure.security | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import io.ticketaka.api.common.ApiError | ||
import jakarta.servlet.http.HttpServletRequest | ||
import jakarta.servlet.http.HttpServletResponse | ||
import org.springframework.security.access.AccessDeniedException | ||
import org.springframework.security.web.access.AccessDeniedHandler | ||
import org.springframework.stereotype.Component | ||
|
||
@Component | ||
class DefaultAccessDeniedHandler( | ||
private val objectMapper: ObjectMapper, | ||
) : AccessDeniedHandler { | ||
override fun handle( | ||
request: HttpServletRequest, | ||
response: HttpServletResponse, | ||
accessDeniedException: AccessDeniedException?, | ||
) { | ||
response.status = HttpServletResponse.SC_FORBIDDEN | ||
val outputStream = response.outputStream | ||
objectMapper | ||
.writeValue(outputStream, ApiError(HttpServletResponse.SC_FORBIDDEN, "접근 권한이 없습니다.")) | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
...kotlin/io/ticketaka/api/common/infrastructure/security/DefaultAuthenticationEntryPoint.kt
This file contains 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,26 @@ | ||
package io.ticketaka.api.common.infrastructure.security | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import io.ticketaka.api.common.ApiError | ||
import jakarta.servlet.http.HttpServletRequest | ||
import jakarta.servlet.http.HttpServletResponse | ||
import org.springframework.security.core.AuthenticationException | ||
import org.springframework.security.web.AuthenticationEntryPoint | ||
import org.springframework.stereotype.Component | ||
|
||
@Component | ||
class DefaultAuthenticationEntryPoint( | ||
private val objectMapper: ObjectMapper, | ||
) : AuthenticationEntryPoint { | ||
override fun commence( | ||
request: HttpServletRequest, | ||
response: HttpServletResponse, | ||
authException: AuthenticationException?, | ||
) { | ||
response.status = HttpServletResponse.SC_UNAUTHORIZED | ||
response.setHeader("content-type", "application/json;charset=utf8") | ||
val outputStream = response.outputStream | ||
objectMapper | ||
.writeValue(outputStream, ApiError(HttpServletResponse.SC_UNAUTHORIZED, "인증되지 않은 사용자입니다.")) | ||
} | ||
} |
90 changes: 90 additions & 0 deletions
90
src/main/kotlin/io/ticketaka/api/common/infrastructure/security/SecurityConfig.kt
This file contains 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,90 @@ | ||
package io.ticketaka.api.common.infrastructure.security | ||
|
||
import io.ticketaka.api.user.application.CustomOAuth2UserService | ||
import io.ticketaka.api.user.application.CustomOidcUserService | ||
import io.ticketaka.api.user.infrastructure.CustomAuthorityMapper | ||
import io.ticketaka.api.user.infrastructure.jwt.JwtAuthenticationFilter | ||
import io.ticketaka.api.user.infrastructure.jwt.JwtExceptionFilter | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity | ||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper | ||
import org.springframework.security.web.AuthenticationEntryPoint | ||
import org.springframework.security.web.SecurityFilterChain | ||
import org.springframework.security.web.access.AccessDeniedHandler | ||
import org.springframework.security.web.access.ExceptionTranslationFilter | ||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter | ||
|
||
@Configuration | ||
@EnableWebSecurity(debug = true) | ||
@EnableMethodSecurity | ||
class SecurityConfig( | ||
private val accessDeniedHandler: AccessDeniedHandler, | ||
private val authenticationEntryPoint: AuthenticationEntryPoint, | ||
private val customOAuth2UserService: CustomOAuth2UserService, | ||
private val customOidcUserService: CustomOidcUserService, | ||
private val jwtExceptionFilter: JwtExceptionFilter, | ||
private val jwtAuthenticationFilter: JwtAuthenticationFilter, | ||
) { | ||
/* | ||
* | ||
Security filter chain: [ | ||
DisableEncodeUrlFilter | ||
WebAsyncManagerIntegrationFilter | ||
SecurityContextHolderFilter | ||
HeaderWriterFilter | ||
CorsFilter | ||
LogoutFilter | ||
OAuth2AuthorizationRequestRedirectFilter | ||
OAuth2LoginAuthenticationFilter | ||
JwtAuthenticationFilter | ||
DefaultLoginPageGeneratingFilter | ||
DefaultLogoutPageGeneratingFilter | ||
RequestCacheAwareFilter | ||
SecurityContextHolderAwareRequestFilter | ||
AnonymousAuthenticationFilter | ||
SessionManagementFilter | ||
JwtExceptionFilter | ||
ExceptionTranslationFilter | ||
AuthorizationFilter | ||
] | ||
* */ | ||
@Bean | ||
fun filterChain(http: HttpSecurity): SecurityFilterChain { | ||
http | ||
.csrf { it.disable() } | ||
// .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) } | ||
.authorizeHttpRequests { it.anyRequest().permitAll() } // TODO oauth 구현이후 endpoint별 권한 설정 필요 | ||
.formLogin { | ||
it | ||
.loginPage("/login") | ||
.loginProcessingUrl("/loginProc") | ||
.defaultSuccessUrl("/") | ||
.permitAll() | ||
}.oauth2Login { oauth2LoginCustomizer -> | ||
oauth2LoginCustomizer.userInfoEndpoint { userInfoEndpointConfig -> | ||
userInfoEndpointConfig | ||
.userService(customOAuth2UserService) | ||
// .oidcUserService(null) | ||
} | ||
}.exceptionHandling { it.authenticationEntryPoint(LoginUrlAuthenticationEntryPoint("/login")) } | ||
.logout { it.logoutSuccessUrl("/") } | ||
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java) | ||
.addFilterBefore(jwtExceptionFilter, ExceptionTranslationFilter::class.java) | ||
return http.build() | ||
} | ||
|
||
@Bean | ||
fun exceptionTranslationFilter(): ExceptionTranslationFilter { | ||
val filter = ExceptionTranslationFilter(authenticationEntryPoint) | ||
filter.setAccessDeniedHandler(accessDeniedHandler) | ||
return filter | ||
} | ||
|
||
@Bean | ||
fun customAuthorityMapper(): GrantedAuthoritiesMapper = CustomAuthorityMapper() | ||
} |
This file contains 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
41 changes: 41 additions & 0 deletions
41
src/main/kotlin/io/ticketaka/api/user/application/CustomOAuth2UserService.kt
This file contains 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,41 @@ | ||
package io.ticketaka.api.user.application | ||
|
||
import io.ticketaka.api.user.domain.ProviderUser | ||
import io.ticketaka.api.user.domain.User | ||
import io.ticketaka.api.user.domain.UserRepository | ||
import io.ticketaka.api.user.infrastructure.oauth2.PrincipalUser | ||
import io.ticketaka.api.user.infrastructure.oauth2.ProviderUserRequest | ||
import io.ticketaka.api.user.infrastructure.oauth2.converter.ProviderUserConverter | ||
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService | ||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest | ||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService | ||
import org.springframework.security.oauth2.core.user.OAuth2User | ||
import org.springframework.stereotype.Service | ||
|
||
@Service | ||
class CustomOAuth2UserService( | ||
private val userRepository: UserRepository, | ||
private val providerUserConverter: ProviderUserConverter<ProviderUserRequest, ProviderUser>, | ||
) : OAuth2UserService<OAuth2UserRequest, OAuth2User> { | ||
override fun loadUser(userRequest: OAuth2UserRequest): OAuth2User { | ||
val clientRegistration = userRequest.clientRegistration | ||
val oAuth2UserService = DefaultOAuth2UserService() | ||
val oAuth2User = oAuth2UserService.loadUser(userRequest) | ||
|
||
val providerUserRequest = ProviderUserRequest(clientRegistration, oAuth2User) | ||
val providerUser = | ||
providerUserConverter.convert(providerUserRequest) | ||
?: throw IllegalArgumentException("Not supported provider") | ||
|
||
registerIfAbsent(providerUser) | ||
|
||
return PrincipalUser(providerUser) | ||
} | ||
|
||
private fun registerIfAbsent(providerUser: ProviderUser) { | ||
val user = userRepository.findByEmail(providerUser.getEmail()) | ||
if (user == null) { | ||
userRepository.save(User.newInstance(providerUser.getEmail())) | ||
} | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
src/main/kotlin/io/ticketaka/api/user/application/CustomOidcUserService.kt
This file contains 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,6 @@ | ||
package io.ticketaka.api.user.application | ||
|
||
import org.springframework.stereotype.Service | ||
|
||
@Service | ||
class CustomOidcUserService |
This file contains 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,7 @@ | ||
package io.ticketaka.api.user.domain | ||
|
||
data class Attributes( | ||
val mainAttributes: Map<String, Any>, | ||
val subAttributes: Map<String, Any> = emptyMap(), | ||
val otherAttributes: Map<String, Any> = emptyMap(), | ||
) |
6 changes: 6 additions & 0 deletions
6
src/main/kotlin/io/ticketaka/api/user/domain/AuthenticatedUser.kt
This file contains 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,6 @@ | ||
package io.ticketaka.api.user.domain | ||
|
||
data class AuthenticatedUser( | ||
val userId: Long, | ||
val roles: Set<Role>, | ||
) |
19 changes: 19 additions & 0 deletions
19
src/main/kotlin/io/ticketaka/api/user/domain/ProviderUser.kt
This file contains 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,19 @@ | ||
package io.ticketaka.api.user.domain | ||
|
||
import org.springframework.security.core.GrantedAuthority | ||
|
||
interface ProviderUser { | ||
fun getId(): String | ||
|
||
fun getUsername(): String | ||
|
||
fun getPassword(): String | ||
|
||
fun getEmail(): String | ||
|
||
fun getProvider(): String | ||
|
||
fun getRoles(): List<GrantedAuthority> | ||
|
||
fun getAttributes(): Map<String, Any> | ||
} |
This file contains 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: 4 additions & 0 deletions
4
src/main/kotlin/io/ticketaka/api/user/domain/UserRepository.kt
This file contains 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,5 +1,9 @@ | ||
package io.ticketaka.api.user.domain | ||
|
||
interface UserRepository { | ||
fun save(user: User): User | ||
|
||
fun findById(id: Long): User? | ||
|
||
fun findByEmail(email: String): User? | ||
} |
2 changes: 1 addition & 1 deletion
2
src/main/kotlin/io/ticketaka/api/user/domain/token/TokenExtractor.kt
This file contains 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,5 +1,5 @@ | ||
package io.ticketaka.api.user.domain.token | ||
|
||
interface TokenExtractor { | ||
fun extract(payload: String): String | ||
fun extract(payload: String?): String | ||
} |
Oops, something went wrong.