diff --git a/src/main/kotlin/com/KY/KoreanYoutube/config/RedisConfig.kt b/src/main/kotlin/com/KY/KoreanYoutube/config/RedisConfig.kt index c1c1694..05d9583 100644 --- a/src/main/kotlin/com/KY/KoreanYoutube/config/RedisConfig.kt +++ b/src/main/kotlin/com/KY/KoreanYoutube/config/RedisConfig.kt @@ -4,10 +4,14 @@ import lombok.RequiredArgsConstructor import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory import org.springframework.data.redis.connection.RedisConnectionFactory import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory +import org.springframework.data.redis.core.ReactiveRedisTemplate import org.springframework.data.redis.core.RedisTemplate import org.springframework.data.redis.repository.configuration.EnableRedisRepositories +import org.springframework.data.redis.serializer.RedisSerializationContext +import org.springframework.data.redis.serializer.RedisSerializationContext.RedisSerializationContextBuilder import org.springframework.data.redis.serializer.StringRedisSerializer @Configuration(value = "redisConfig") @@ -16,10 +20,10 @@ import org.springframework.data.redis.serializer.StringRedisSerializer class RedisConfig { @Value("\${spring.data.redis.host}") - lateinit var host : String + lateinit var host: String @Value("\${spring.data.redis.port}") - var port : Int = 6379 + var port: Int = 6379 @Bean @@ -29,6 +33,13 @@ class RedisConfig { return lettuceConnectionFactory } + @Bean + fun reactiveRedisConnectionFactory(): ReactiveRedisConnectionFactory { + val lettuceConnectionFactory = LettuceConnectionFactory(host, port) + lettuceConnectionFactory.start() + return lettuceConnectionFactory + } + @Bean fun redisTemplate(): RedisTemplate { val redisTemplate = RedisTemplate() @@ -38,4 +49,15 @@ class RedisConfig { redisTemplate.afterPropertiesSet() return redisTemplate } + +// @Bean +// fun reactiveRedisTemplate(): ReactiveRedisTemplate { +// val serializationContext = +// RedisSerializationContext.newSerializationContext() +// .key(StringRedisSerializer()) +// .value(StringRedisSerializer()) +// .build() +// return ReactiveRedisTemplate(reactiveRedisConnectionFactory(), serializationContext) +// +// } } \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/config/SecurityConfig.kt b/src/main/kotlin/com/KY/KoreanYoutube/config/SecurityConfig.kt index a08ff81..75cee32 100644 --- a/src/main/kotlin/com/KY/KoreanYoutube/config/SecurityConfig.kt +++ b/src/main/kotlin/com/KY/KoreanYoutube/config/SecurityConfig.kt @@ -1,69 +1,82 @@ package com.KY.KoreanYoutube.config import com.KY.KoreanYoutube.security.JwtAuthenticationFilter -import com.KY.KoreanYoutube.security.OAuthSuccessHandler -import com.KY.KoreanYoutube.security.PrincipalOauthUserService +import com.KY.KoreanYoutube.security_reactive.JwtAuthenticationFilterReactive +import com.KY.KoreanYoutube.security_reactive.OAuthSuccessHandlerReactive +import com.KY.KoreanYoutube.security_reactive.PrincipalOauthUserServiceReactive import mu.KotlinLogging -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.context.annotation.Import -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.invoke +import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity +import org.springframework.security.config.web.server.SecurityWebFiltersOrder +import org.springframework.security.config.web.server.ServerHttpSecurity import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.security.crypto.password.PasswordEncoder -import org.springframework.security.web.SecurityFilterChain -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter -import org.springframework.security.web.server.context.NoOpServerSecurityContextRepository +import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository +import org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver +import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver +import org.springframework.security.web.server.SecurityWebFilterChain +import org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint +import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher val log = KotlinLogging.logger{} -@EnableWebSecurity -@Import(DataSourceAutoConfiguration::class) + @Configuration +@EnableReactiveMethodSecurity +@EnableWebFluxSecurity class SecurityConfig( - val principalOauthUserService: PrincipalOauthUserService, - val oAuthSuccessHandler: OAuthSuccessHandler, - val jwtAuthenticationFilter: JwtAuthenticationFilter + val principalOauthUserService: PrincipalOauthUserServiceReactive, + val oAuthSuccessHandler: OAuthSuccessHandlerReactive, + val jwtAuthenticationFilter: JwtAuthenticationFilterReactive, + val clientRegistrationRepository: ReactiveClientRegistrationRepository ) { @Bean - fun filterChain(http: HttpSecurity): SecurityFilterChain { - http { // kotlin DSL - httpBasic { disable() } - csrf { disable() } - cors { } - authorizeRequests { - authorize("/api/v1/stream/done", permitAll) - authorize("/api/v1/stream/verify", permitAll) - authorize("/api/v1/stream/live/**",permitAll) - authorize("/api/v1/stream/**",authenticated) - authorize("/api/v1/upload/**",authenticated) - authorize("/**",permitAll) + fun filterChain(http: ServerHttpSecurity): SecurityWebFilterChain { + http.httpBasic { + it.disable() } - oauth2Login { - loginPage = "/loginPage" - defaultSuccessUrl("/",true) - userInfoEndpoint { - userService = principalOauthUserService - } - authenticationSuccessHandler = oAuthSuccessHandler + .csrf { + it.disable() } - - exceptionHandling { - //authenticationEntryPoint = serverAuthenticationEntryPoint() + .authorizeExchange{ + it.pathMatchers("/api/v1/stream/done").permitAll() + it.pathMatchers("/api/v1/stream/verify").permitAll() + it.pathMatchers("/api/v1/stream/live/**") .permitAll() + it.pathMatchers("/api/v1/stream/**").authenticated() + it.pathMatchers("/api/v1/upload/**").authenticated() + it.pathMatchers("/**").permitAll() } - addFilterBefore(jwtAuthenticationFilter) - - - } + .exceptionHandling{ + it.authenticationEntryPoint(RedirectServerAuthenticationEntryPoint("/loginPage")) + } + .oauth2Login { + it.authenticationSuccessHandler(oAuthSuccessHandler) + it.authorizationRequestResolver(authorizationRequestResolver()) + } + .addFilterBefore(jwtAuthenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION) return http.build() } @Bean fun passwordEncoder(): PasswordEncoder { return BCryptPasswordEncoder() } + + private fun authorizationRequestResolver(): ServerOAuth2AuthorizationRequestResolver { + val authorizationRequestMatcher: ServerWebExchangeMatcher = PathPatternParserServerWebExchangeMatcher( + "/oauth2/authorization/{registrationId}" + ) + + return DefaultServerOAuth2AuthorizationRequestResolver( + clientRegistrationRepository, authorizationRequestMatcher + ) + } + + // private fun serverAuthenticationEntryPoint(): AuthenticationEntryPoint? { // return AuthenticationEntryPoint { request,response, authEx: AuthenticationException -> // val requestPath = request.pathInfo diff --git a/src/main/kotlin/com/KY/KoreanYoutube/config/WebConfig.kt b/src/main/kotlin/com/KY/KoreanYoutube/config/WebConfig.kt index 08c59cb..d5ead14 100644 --- a/src/main/kotlin/com/KY/KoreanYoutube/config/WebConfig.kt +++ b/src/main/kotlin/com/KY/KoreanYoutube/config/WebConfig.kt @@ -1,18 +1,18 @@ package com.KY.KoreanYoutube.config - -import org.springframework.context.annotation.Configuration -import org.springframework.web.servlet.config.annotation.CorsRegistry -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer - - -@Configuration -class WebConfig : WebMvcConfigurer { - override fun addCorsMappings(registry: CorsRegistry) { - registry.addMapping("/**") - .allowedOrigins("*") - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") - .allowedHeaders("*") - .allowCredentials(false) - .maxAge(3600) - } -} \ No newline at end of file +// +//import org.springframework.context.annotation.Configuration +//import org.springframework.web.servlet.config.annotation.CorsRegistry +//import org.springframework.web.servlet.config.annotation.WebMvcConfigurer +// +// +//@Configuration +//class WebConfig : WebMvcConfigurer { +// override fun addCorsMappings(registry: CorsRegistry) { +// registry.addMapping("/**") +// .allowedOrigins("*") +// .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") +// .allowedHeaders("*") +// .allowCredentials(false) +// .maxAge(3600) +// } +//} \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/config/WebFluxConfig.kt b/src/main/kotlin/com/KY/KoreanYoutube/config/WebFluxConfig.kt new file mode 100644 index 0000000..72f6bd6 --- /dev/null +++ b/src/main/kotlin/com/KY/KoreanYoutube/config/WebFluxConfig.kt @@ -0,0 +1,68 @@ +package com.KY.KoreanYoutube.config + +import org.springframework.context.ApplicationContext +import org.springframework.context.ApplicationContextAware +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.web.reactive.config.CorsRegistry +import org.springframework.web.reactive.config.EnableWebFlux +import org.springframework.web.reactive.config.ViewResolverRegistry +import org.springframework.web.reactive.config.WebFluxConfigurer +import org.thymeleaf.spring6.ISpringWebFluxTemplateEngine +import org.thymeleaf.spring6.SpringWebFluxTemplateEngine +import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver +import org.thymeleaf.spring6.view.reactive.ThymeleafReactiveViewResolver +import org.thymeleaf.templatemode.TemplateMode + + +@Configuration +@EnableWebFlux +class WebFluxConfig( + private var ctx : ApplicationContext +) : ApplicationContextAware, WebFluxConfigurer{ + + override fun setApplicationContext(applicationContext: ApplicationContext) { + ctx = applicationContext + } + @Bean + fun thymeleafTemplateResolver(): SpringResourceTemplateResolver { + val resolver = SpringResourceTemplateResolver() + resolver.setApplicationContext(this.ctx) + resolver.prefix = "classpath:/templates/" + resolver.suffix = ".html" + resolver.templateMode = TemplateMode.HTML + resolver.isCacheable = false + resolver.checkExistence = false + return resolver + } + + @Bean + fun thymeleafTemplateEngine(): ISpringWebFluxTemplateEngine { + val templateEngine = SpringWebFluxTemplateEngine() + templateEngine.setTemplateResolver(thymeleafTemplateResolver()) + return templateEngine + } + + @Bean + fun thymeleafChunkedAndDataDrivenViewResolver(): ThymeleafReactiveViewResolver { + val viewResolver = ThymeleafReactiveViewResolver() + viewResolver.templateEngine = thymeleafTemplateEngine() + viewResolver.responseMaxChunkSizeBytes = 8192 + return viewResolver + } + + override fun configureViewResolvers(registry: ViewResolverRegistry) { + registry.viewResolver(thymeleafChunkedAndDataDrivenViewResolver()) + } + + override fun addCorsMappings(registry: CorsRegistry) { + registry.addMapping("/**") + .allowedOrigins("*") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*") + .allowCredentials(false) + .maxAge(3600) + } + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/dto/VideoDetailResponseDTO.kt b/src/main/kotlin/com/KY/KoreanYoutube/dto/VideoDetailResponseDTO.kt new file mode 100644 index 0000000..dbac747 --- /dev/null +++ b/src/main/kotlin/com/KY/KoreanYoutube/dto/VideoDetailResponseDTO.kt @@ -0,0 +1,3 @@ +package com.KY.KoreanYoutube.dto + +data class VideoDetailResponseDTO() diff --git a/src/main/kotlin/com/KY/KoreanYoutube/main/MainController.kt b/src/main/kotlin/com/KY/KoreanYoutube/main/MainController.kt index 299177d..e428589 100644 --- a/src/main/kotlin/com/KY/KoreanYoutube/main/MainController.kt +++ b/src/main/kotlin/com/KY/KoreanYoutube/main/MainController.kt @@ -1,18 +1,20 @@ package com.KY.KoreanYoutube.main -import com.KY.KoreanYoutube.config.log import com.KY.KoreanYoutube.security.JwtTokenProvider import com.KY.KoreanYoutube.security.logger -import com.KY.KoreanYoutube.user.UserService -import jakarta.servlet.http.HttpServletRequest +import com.KY.KoreanYoutube.user.UserR2DBCService import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus.MOVED_PERMANENTLY import org.springframework.http.ResponseEntity import org.springframework.stereotype.Controller import org.springframework.ui.Model import org.springframework.web.bind.annotation.GetMapping + import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.ResponseBody +import reactor.core.publisher.Mono +import reactor.kotlin.core.publisher.onErrorReturn import java.net.URI @Controller @@ -20,33 +22,40 @@ import java.net.URI class MainController( val mainService: MainService, val jwtTokenProvider: JwtTokenProvider, - val userService: UserService + val userService: UserR2DBCService ) { @GetMapping("/") - fun mainPage(model : Model,@RequestParam(required = false) token : String?): String { - val mainData = mainService.getMainPage() - model.addAllAttributes(mainData) - if(!token.isNullOrEmpty()){ - logger.info { "++++++++++++++++++++++++++++++로그인됨" } - val name = jwtTokenProvider.getAuthentication(token).name - val user = userService.findByUserId(name) - if(user !=null){ - model.addAttribute("user",user.name) - model.addAttribute("jwt",token) - model.addAttribute("isLogined", "true") - } - else{ - model.addAttribute("user","null") - model.addAttribute("isLogined", "false") - } - } - else{ - model.addAttribute("user","null") - model.addAttribute("isLogined", "false") - } - return "main" - + fun mainPage(model : Model,@RequestParam(required = false) token : String?): Mono { + logger.info { "TESTCCCCCCCXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" } + logger.info{token} + return mainService.getMainPage() + .doOnNext {mainData-> + model.addAllAttributes(mainData) + logger.info{mainData} + } + .doOnNext { + checkNotNull(token) + check(token.isNotEmpty()) + } + .flatMap { + val name = jwtTokenProvider.getAuthentication(token!!).name + userService.findByUserId(name) + }.doOnNext {user-> + checkNotNull(user) + } + .doOnNext {user-> + model.addAttribute("user",user.name) + model.addAttribute("jwt",token) + model.addAttribute("isLogined", "true") + } + .doOnError { + logger.info { "NULL" } + model.addAttribute("user","null") + model.addAttribute("isLogined", "false") + } + .thenReturn("main") + .onErrorReturn("main") } @GetMapping("/access/google") diff --git a/src/main/kotlin/com/KY/KoreanYoutube/main/MainService.kt b/src/main/kotlin/com/KY/KoreanYoutube/main/MainService.kt index 250c3e7..b77aa7d 100644 --- a/src/main/kotlin/com/KY/KoreanYoutube/main/MainService.kt +++ b/src/main/kotlin/com/KY/KoreanYoutube/main/MainService.kt @@ -1,6 +1,7 @@ package com.KY.KoreanYoutube.main import com.KY.KoreanYoutube.domain.VideoR2dbc +import com.KY.KoreanYoutube.main import com.KY.KoreanYoutube.stream.StreamService import com.KY.KoreanYoutube.video.VideoR2DBCRepository import com.KY.KoreanYoutube.video.VideoRepository @@ -17,19 +18,18 @@ class MainService( val videoService: VideoService, val streamService: StreamService ) { - fun getMainPage(): HashMap { + fun getMainPage(): Mono> { val mainData = hashMapOf() - mainData["videoList"] = videoService.findAll(Sort.by("createDate").descending()).collectList().block()?: run{listOf()} - mainData["streamList"] = streamService.findAllOnAir() - - return mainData -// val empty = Flux.empty() -// return Flux.zip(videoList,empty) -// .map { -// val hashMap = hashMapOf() -// hashMap["videoList"] = it.t1 -// hashMap -// } -// .toMono() + return videoService.findAll(Sort.by("createDate").descending()).collectList() + .doOnNext { + mainData["videoList"] = it + } + .map { + streamService.findAllOnAir() + } + .doOnNext { + mainData["streamList"] = it + } + .thenReturn(mainData) } } \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/redis/RedisR2dbcRepository.kt b/src/main/kotlin/com/KY/KoreanYoutube/redis/RedisR2dbcRepository.kt new file mode 100644 index 0000000..fa4603f --- /dev/null +++ b/src/main/kotlin/com/KY/KoreanYoutube/redis/RedisR2dbcRepository.kt @@ -0,0 +1,34 @@ +package com.KY.KoreanYoutube.redis + +import com.fasterxml.jackson.databind.ObjectMapper +import org.springframework.data.redis.core.ReactiveRedisTemplate +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono +import java.time.Duration + + +@Repository +class RedisR2dbcRepository( + private val redisTemplate : ReactiveRedisTemplate, + private val mapper: ObjectMapper +) { + + fun save(key : String, value : String, duration: Duration): Mono { + return redisTemplate.opsForValue().set(key,value, duration) + } + + fun load(key:String): Mono { + return redisTemplate.opsForValue().get(key) + } + + fun load(key : String, type : Class): Mono { + return redisTemplate.opsForValue() + .get(key) + .map { value-> mapper.readValue(value,type) } + } + + fun removeRtmp(streamKey: String): Mono { + return redisTemplate.opsForValue().getAndDelete(streamKey) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/redis/RedisR2dbcService.kt b/src/main/kotlin/com/KY/KoreanYoutube/redis/RedisR2dbcService.kt new file mode 100644 index 0000000..43148b9 --- /dev/null +++ b/src/main/kotlin/com/KY/KoreanYoutube/redis/RedisR2dbcService.kt @@ -0,0 +1,51 @@ +package com.KY.KoreanYoutube.redis + +import com.KY.KoreanYoutube.domain.User +import com.KY.KoreanYoutube.domain.UserR2dbc +import com.KY.KoreanYoutube.utils.JWT_PREFIX +import com.KY.KoreanYoutube.utils.RTMP_ING_PREFIX +import com.KY.KoreanYoutube.utils.RTMP_PREFIX +import com.fasterxml.jackson.databind.ObjectMapper +import org.springframework.stereotype.Service +import reactor.core.publisher.Mono +import java.time.Duration + + +@Service +class RedisR2dbcService ( + private val redisRepository: RedisR2dbcRepository, + private val mapper: ObjectMapper +) { + + fun saveRtmp(streamKey : String): Mono { + return redisRepository.save(RTMP_PREFIX + streamKey,"off", Duration.ofMinutes(10)) + } + + fun loadRtmp(streamKey: String): Mono { + return redisRepository.load(RTMP_PREFIX + streamKey) + } + + fun loadRtmpAndRemove(streamKey: String): Mono { + return redisRepository.removeRtmp(RTMP_PREFIX + streamKey) + } + + + fun saveJwt(jwt: String, user: UserR2dbc): Mono { + return redisRepository.save(JWT_PREFIX +jwt,mapper.writeValueAsString(user), Duration.ofHours(3)) + } + + fun loadByJwt(jwt: String): Mono { + return redisRepository.load(JWT_PREFIX +jwt, User::class.java) + } + + fun saveRtmpIng(key: String): Mono { + return redisRepository.save(RTMP_ING_PREFIX + key,"live", Duration.ofHours(12)) + } + + fun doneRtmpIng(streamKey: String): Mono { + return redisRepository.removeRtmp(RTMP_ING_PREFIX +streamKey) + } + + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/security/OAuthSuccessHandler.kt b/src/main/kotlin/com/KY/KoreanYoutube/security/OAuthSuccessHandler.kt index 68a5636..0411b3d 100644 --- a/src/main/kotlin/com/KY/KoreanYoutube/security/OAuthSuccessHandler.kt +++ b/src/main/kotlin/com/KY/KoreanYoutube/security/OAuthSuccessHandler.kt @@ -12,7 +12,7 @@ import org.springframework.stereotype.Component val logger = KotlinLogging.logger { } -@Component(value = "authenticationSuccessHandler") +@Component class OAuthSuccessHandler( val userService: UserService, val redisService: RedisService, diff --git a/src/main/kotlin/com/KY/KoreanYoutube/security/OAuthSuccessHandlerReactive.kt b/src/main/kotlin/com/KY/KoreanYoutube/security/OAuthSuccessHandlerReactive.kt deleted file mode 100644 index 3c0946b..0000000 --- a/src/main/kotlin/com/KY/KoreanYoutube/security/OAuthSuccessHandlerReactive.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.KY.KoreanYoutube.security - -import com.KY.KoreanYoutube.domain.User -import com.KY.KoreanYoutube.user.UserService -import jakarta.servlet.http.HttpServletRequest -import jakarta.servlet.http.HttpServletResponse -import jakarta.transaction.Transactional -import mu.KotlinLogging -import org.apache.http.impl.client.DefaultRedirectStrategy -import org.springframework.http.HttpStatus -import org.springframework.security.core.Authentication -import org.springframework.security.oauth2.core.OAuth2AccessToken -import org.springframework.security.oauth2.core.user.OAuth2User -import org.springframework.security.web.authentication.AuthenticationSuccessHandler -import org.springframework.security.web.server.DefaultServerRedirectStrategy -import org.springframework.security.web.server.WebFilterExchange -import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler -import org.springframework.stereotype.Component -import org.springframework.web.util.UriComponentsBuilder -import reactor.core.publisher.Mono - - -//@Component(value = "authenticationSuccessHandler") -//class OAuthSuccessHandlerReactive( -// val userService: UserService, -// val redisRepository: RedisRepository, -// val jwtTokenProvider: JwtTokenProvider, -//) : ServerAuthenticationSuccessHandler { -// override fun onAuthenticationSuccess( -// webFilterExchange: WebFilterExchange, -// authentication: Authentication -// ): Mono? { -// println("TTTTTTTTTTTTTTTTTTTTTTTTTT") -// val principal = authentication.principal as PrincipalDetails -// val userName = principal.getProvider().name +"_"+ principal.getEmail() -// val jwt = jwtTokenProvider.createToken(userName) -// return userService.findUserByName(userName) -// .doOnNext {user -> -// redisRepository.save(jwt.token,user!!) -// } -// .flatMap { -// val uri = UriComponentsBuilder.newInstance().scheme("http").host("localhost:8080").path("/access/google").queryParam("code",jwt.token).build().toUri() -// val exchange = webFilterExchange.exchange -// DefaultServerRedirectStrategy().sendRedirect(exchange,uri) -// } -// -// -// } -//} \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/security/PrincipalOauthUserService.kt b/src/main/kotlin/com/KY/KoreanYoutube/security/PrincipalOauthUserService.kt index d759211..79b3e57 100644 --- a/src/main/kotlin/com/KY/KoreanYoutube/security/PrincipalOauthUserService.kt +++ b/src/main/kotlin/com/KY/KoreanYoutube/security/PrincipalOauthUserService.kt @@ -10,80 +10,80 @@ import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -@Service -class PrincipalOauthUserService( - private val userService: UserService, -) : DefaultOAuth2UserService() { - - //구글로 부터 받은 userRequest 데이터에 대한 후처리되는 함수 - //함수 종료시 @AuthenticationPrincipal 어노테이션이 만들어진다. - @Throws(OAuth2AuthenticationException::class) - @Transactional(value = "transactionManager") - override fun loadUser(userRequest: OAuth2UserRequest): OAuth2User? { - println("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") - //"registraionId" 로 어떤 OAuth 로 로그인 했는지 확인 가능(google,naver등) - - println("getClientRegistration: " + userRequest.clientRegistration) - println("getAccessToken: " + userRequest.accessToken.tokenValue) - println("getAttributes: " + super.loadUser(userRequest).attributes) - - - //구글 로그인 버튼 클릭 -> 구글 로그인창 -> 로그인 완료 -> code를 리턴(OAuth-Clien라이브러리가 받아줌) -> code를 통해서 AcssToken요청(access토큰 받음) - // => "userRequest"가 감고 있는 정보 - //회원 프로필을 받아야하는데 여기서 사용되는것이 "loadUser" 함수이다 -> 구글 로 부터 회원 프로필을 받을수 있다. - /** - * OAuth 로그인 회원 가입 - */ - val oAuth2User = super.loadUser(userRequest) - var oAuth2UserInfo: OAuth2UserInfo? = null - - when (userRequest.clientRegistration.registrationId) { - "google" -> { - oAuth2UserInfo = GoogleUserInfo(oAuth2User.attributes) - } - "naver" -> { - oAuth2UserInfo = NaverUserInfo(oAuth2User.attributes["response"] as Map) - } - "kakao" -> { - oAuth2UserInfo = KakaoUserInfo( - oAuth2User.attributes["kakao_account"] as Map, - oAuth2User.attributes["id"].toString() - ) - } - else -> { - println("지원하지 않은 로그인 서비스 입니다.") - } - } - - if(oAuth2UserInfo==null){ - return null - } - - val provider: OAuthProvider = oAuth2UserInfo.provider //google , naver, facebook etc - val providerId: String = oAuth2UserInfo.providerId - val password = providerId //중요하지 않음 그냥 패스워드 암호화 하 - val email: String = oAuth2UserInfo.email - val userId = provider.name + "_" + email - val name = oAuth2UserInfo.name - println("AAAAAAAAAAAAAAAAAAAA"+userId) - val role: Role = Role.USER - - - - var userEntity: User? = userService.findByUserId(userId) - //처음 서비스를 이용한 회원일 경우 - if (userEntity == null) { - userEntity = User( - userId = userId, - name = name, - password = password, - email = email, - role = role, - provider = provider - ) - userService.save(userEntity) - } - - return PrincipalDetails(userEntity, oAuth2User.attributes) - } -} \ No newline at end of file +//@Service +//class PrincipalOauthUserService( +// private val userService: UserService, +//) : DefaultOAuth2UserService() { +// +// //구글로 부터 받은 userRequest 데이터에 대한 후처리되는 함수 +// //함수 종료시 @AuthenticationPrincipal 어노테이션이 만들어진다. +// @Throws(OAuth2AuthenticationException::class) +// @Transactional(value = "transactionManager") +// override fun loadUser(userRequest: OAuth2UserRequest): OAuth2User? { +// println("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") +// //"registraionId" 로 어떤 OAuth 로 로그인 했는지 확인 가능(google,naver등) +// +// println("getClientRegistration: " + userRequest.clientRegistration) +// println("getAccessToken: " + userRequest.accessToken.tokenValue) +// println("getAttributes: " + super.loadUser(userRequest).attributes) +// +// +// //구글 로그인 버튼 클릭 -> 구글 로그인창 -> 로그인 완료 -> code를 리턴(OAuth-Clien라이브러리가 받아줌) -> code를 통해서 AcssToken요청(access토큰 받음) +// // => "userRequest"가 감고 있는 정보 +// //회원 프로필을 받아야하는데 여기서 사용되는것이 "loadUser" 함수이다 -> 구글 로 부터 회원 프로필을 받을수 있다. +// /** +// * OAuth 로그인 회원 가입 +// */ +// val oAuth2User = super.loadUser(userRequest) +// var oAuth2UserInfo: OAuth2UserInfo? = null +// +// when (userRequest.clientRegistration.registrationId) { +// "google" -> { +// oAuth2UserInfo = GoogleUserInfo(oAuth2User.attributes) +// } +// "naver" -> { +// oAuth2UserInfo = NaverUserInfo(oAuth2User.attributes["response"] as Map) +// } +// "kakao" -> { +// oAuth2UserInfo = KakaoUserInfo( +// oAuth2User.attributes["kakao_account"] as Map, +// oAuth2User.attributes["id"].toString() +// ) +// } +// else -> { +// println("지원하지 않은 로그인 서비스 입니다.") +// } +// } +// +// if(oAuth2UserInfo==null){ +// return null +// } +// +// val provider: OAuthProvider = oAuth2UserInfo.provider //google , naver, facebook etc +// val providerId: String = oAuth2UserInfo.providerId +// val password = providerId //중요하지 않음 그냥 패스워드 암호화 하 +// val email: String = oAuth2UserInfo.email +// val userId = provider.name + "_" + email +// val name = oAuth2UserInfo.name +// println("AAAAAAAAAAAAAAAAAAAA"+userId) +// val role: Role = Role.USER +// +// +// +// var userEntity: User? = userService.findByUserId(userId) +// //처음 서비스를 이용한 회원일 경우 +// if (userEntity == null) { +// userEntity = User( +// userId = userId, +// name = name, +// password = password, +// email = email, +// role = role, +// provider = provider +// ) +// userService.save(userEntity) +// } +// +// return PrincipalDetails(userEntity, oAuth2User.attributes) +// } +//} \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/security/PrincipalOauthUserServiceReactive.kt b/src/main/kotlin/com/KY/KoreanYoutube/security/PrincipalOauthUserServiceReactive.kt deleted file mode 100644 index 0feef06..0000000 --- a/src/main/kotlin/com/KY/KoreanYoutube/security/PrincipalOauthUserServiceReactive.kt +++ /dev/null @@ -1,103 +0,0 @@ -package com.KY.KoreanYoutube.security - -import com.KY.KoreanYoutube.domain.User -import com.KY.KoreanYoutube.domain.UserR2dbc -import com.KY.KoreanYoutube.user.UserService -import org.springframework.security.core.userdetails.UserDetails -import org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2UserService -import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest -import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService -import org.springframework.security.oauth2.core.OAuth2AuthenticationException -import org.springframework.security.oauth2.core.user.OAuth2User -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import reactor.core.publisher.Mono -import reactor.kotlin.core.publisher.switchIfEmpty - - -//@Service -//class PrincipalOauthUserServiceReactive( -// private val userService: UserService, -//) : ReactiveOAuth2UserService { -// -// //구글로 부터 받은 userRequest 데이터에 대한 후처리되는 함수 -// //함수 종료시 @AuthenticationPrincipal 어노테이션이 만들어진다. -// @Throws(OAuth2AuthenticationException::class) -// @Transactional(value = "transactionManager") -// override fun loadUser(userRequest: OAuth2UserRequest): Mono { -// //"registraionId" 로 어떤 OAuth 로 로그인 했는지 확인 가능(google,naver등) -// //구글 로그인 버튼 클릭 -> 구글 로그인창 -> 로그인 완료 -> code를 리턴(OAuth-Clien라이브러리가 받아줌) -> code를 통해서 AcssToken요청(access토큰 받음) -// // => "userRequest"가 감고 있는 정보 -// //회원 프로필을 받아야하는데 여기서 사용되는것이 "loadUser" 함수이다 -> 구글 로 부터 회원 프로필을 받을수 있다. -// /** -// * OAuth 로그인 회원 가입 -// */ -// -//// println("getClientRegistration: " + userRequest.clientRegistration) -//// println("getAccessToken: " + userRequest.accessToken.tokenValue) -//// println("getAttributes: " + super.loadUser(userRequest).attributes) -// -// -// return oAuthToUser(userRequest) -// -// } -// -// private fun oAuthToUser(userRequest: OAuth2UserRequest): Mono { -// val delegate = DefaultReactiveOAuth2UserService() -// return delegate.loadUser(userRequest) -// .doOnNext { -// logger.info { "PRINCIPAL 지나감" } -// } -// .map { oAuth2User -> -// var oAuth2UserInfo: OAuth2UserInfo? = null -// when (userRequest.clientRegistration.registrationId) { -// "google" -> { -// oAuth2UserInfo = GoogleUserInfo(oAuth2User.attributes) -// } -// -// "naver" -> { -// oAuth2UserInfo = NaverUserInfo(oAuth2User.attributes["response"] as Map) -// } -// -// "kakao" -> { -// oAuth2UserInfo = KakaoUserInfo( -// oAuth2User.attributes["kakao_account"] as Map, -// oAuth2User.attributes["id"].toString() -// ) -// } -// -// else -> { -// println("지원하지 않은 로그인 서비스 입니다.") -// } -// } -// Pair(oAuth2UserInfo!!, oAuth2User.attributes) -// } -// .flatMap { p -> -// Mono.zip(userInfo2User(p.first), Mono.just(p.second)) -// } -// .map { p -> -// PrincipalDetails(p.t1, p.t2) as OAuth2User -// } -// } -// -// private fun userInfo2User(oAuth2UserInfo : OAuth2UserInfo): Mono { -// val provider: OAuthProvider = oAuth2UserInfo.provider -// val providerId: String = oAuth2UserInfo.providerId -// val password = providerId //중요하지 않음 그냥 패스워드 암호화 하 -// val email: String = oAuth2UserInfo.email -// val username = provider.name + "_" + email -// val role: Role = Role.USER -// -// return userService.findUserByName(username).switchIfEmpty{ -// val user = UserR2dbc( -// name = username, -// password = password, -// email = email, -// role = role, -// provider = provider -// ) -// userService.save(user) -// } -// //처음 서비스를 이용한 회원일 경우 -// } -//} \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/security/PrincipalServiceReactive.kt b/src/main/kotlin/com/KY/KoreanYoutube/security/PrincipalServiceReactive.kt deleted file mode 100644 index 2e577b6..0000000 --- a/src/main/kotlin/com/KY/KoreanYoutube/security/PrincipalServiceReactive.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.KY.KoreanYoutube.security - -import com.KY.KoreanYoutube.domain.User -import com.KY.KoreanYoutube.domain.UserR2dbc -import com.KY.KoreanYoutube.user.UserService -import lombok.RequiredArgsConstructor -import org.springframework.security.core.userdetails.ReactiveUserDetailsService -import org.springframework.security.core.userdetails.UserDetails -import org.springframework.security.core.userdetails.UserDetailsService -import org.springframework.security.core.userdetails.UsernameNotFoundException -import org.springframework.stereotype.Service -import reactor.core.publisher.Mono - - -//@Service -//@RequiredArgsConstructor -//class PrincipalServiceReactive( -// private val userService: UserService -//) : ReactiveUserDetailsService { -// -// -// //시큐리티 session => Authentication => UserDetails -// // 여기서 리턴 된 값이 Authentication 안에 들어간다.(리턴될때 들어간다.) -// // 그리고 시큐리티 session 안에 Authentication 이 들어간다. -// //함수 종료시 @AuthenticationPrincipal 어노테이션이 만들어진다. -// @Throws(UsernameNotFoundException::class) -// override fun findByUsername(username: String): Mono { -// return userService.findUserByName(username) -// .map{ -// return@map PrincipalDetails(it) -// } -// } -//} \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/JwtAuthenticationFilterReactive.kt b/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/JwtAuthenticationFilterReactive.kt new file mode 100644 index 0000000..49e10c7 --- /dev/null +++ b/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/JwtAuthenticationFilterReactive.kt @@ -0,0 +1,41 @@ +package com.KY.KoreanYoutube.security_reactive + +import com.KY.KoreanYoutube.security.JwtTokenProvider +import com.KY.KoreanYoutube.security.logger +import org.springframework.http.server.reactive.ServerHttpRequest +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.stereotype.Component +import org.springframework.util.StringUtils +import org.springframework.web.filter.GenericFilterBean +import org.springframework.web.server.ServerWebExchange +import org.springframework.web.server.WebFilter +import org.springframework.web.server.WebFilterChain +import reactor.core.publisher.Mono + +@Component +class JwtAuthenticationFilterReactive( + private val jwtTokenProvider: JwtTokenProvider +) : WebFilter { + + private fun resolveToken(request : ServerHttpRequest) : String? { + val bearerToken = request.headers["Authorization"]?.get(0) + return if (StringUtils.hasText(bearerToken) && bearerToken!!.startsWith("Bearer")) { + bearerToken.substring(7) + } else { + null + } + } + + override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono { + logger.info {"jwt 필터"} + val a =exchange.request + val token = resolveToken(exchange.request) + if (token != null && jwtTokenProvider.validateToken(token)) { + val authentication = jwtTokenProvider.getAuthentication(token) + SecurityContextHolder.getContext().authentication = authentication + logger.info("doFilterChain:$authentication") + } + return chain.filter(exchange) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/OAuthSuccessHandlerReactive.kt b/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/OAuthSuccessHandlerReactive.kt new file mode 100644 index 0000000..cd8b31a --- /dev/null +++ b/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/OAuthSuccessHandlerReactive.kt @@ -0,0 +1,53 @@ +package com.KY.KoreanYoutube.security_reactive + +import com.KY.KoreanYoutube.redis.RedisR2dbcService +import com.KY.KoreanYoutube.security.JwtTokenProvider +import com.KY.KoreanYoutube.security.PrincipalDetails +import com.KY.KoreanYoutube.security.logger +import com.KY.KoreanYoutube.user.UserR2DBCService +import org.springframework.security.core.Authentication +import org.springframework.security.web.server.DefaultServerRedirectStrategy +import org.springframework.security.web.server.WebFilterExchange +import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler +import org.springframework.stereotype.Component +import org.springframework.web.util.UriComponentsBuilder +import reactor.core.publisher.Mono + + +@Component(value = "authenticationSuccessHandler") +class OAuthSuccessHandlerReactive( + val userService: UserR2DBCService, + val redisService: RedisR2dbcService, + val jwtTokenProvider: JwtTokenProvider, +) : ServerAuthenticationSuccessHandler { + override fun onAuthenticationSuccess( + webFilterExchange: WebFilterExchange, + authentication: Authentication + ): Mono { + logger.info("TTTTTTTTTTTTTTTTTTTTTTTTTT") + val principal = authentication.principal as PrincipalDetailsReactive + val userName = principal.getProvider().name +"_"+ principal.getEmail() + val jwt = jwtTokenProvider.createToken(userName) + return userService.findByUserId(userName) + .doOnNext { + logger.info { "AAAAAAAAAAAAAAAAAAAA" } + } + .flatMap {user -> + redisService.saveJwt(jwt.token,user!!) + } + .doOnNext { + logger.info { "SSSSSSSSSSSSS$it" } + } + .filter { it == true } + .doOnNext { + logger.info { "여기는 success 헨들러" } + } + .flatMap { + val uri = UriComponentsBuilder.newInstance().path("/").queryParam("token",jwt.token).build().toUri() + val exchange = webFilterExchange.exchange + DefaultServerRedirectStrategy().sendRedirect(exchange,uri) + } + + + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/PrincipalDetailsReactive.kt b/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/PrincipalDetailsReactive.kt new file mode 100644 index 0000000..6d20683 --- /dev/null +++ b/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/PrincipalDetailsReactive.kt @@ -0,0 +1,65 @@ +package com.KY.KoreanYoutube.security_reactive + +import com.KY.KoreanYoutube.domain.UserR2dbc +import com.KY.KoreanYoutube.security.OAuthProvider +import org.springframework.security.core.GrantedAuthority +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.security.oauth2.core.user.OAuth2User + + +class PrincipalDetailsReactive : UserDetails, OAuth2User{ + private var user : UserR2dbc + private lateinit var attributes :MutableMap + public constructor(user: UserR2dbc) { + this.user = user + } + + constructor(user: UserR2dbc, attributes: Map){ + this.user = user + this.attributes = attributes as MutableMap + } + + override fun getName(): String { + return user.userId + } + + override fun getAttributes(): MutableMap { + return attributes + } + + override fun getAuthorities(): Collection { + val collection: ArrayList = ArrayList() + collection.add(GrantedAuthority { java.lang.String.valueOf(user.role) }) + return collection + } + override fun getPassword(): String { + return user.password + } + + override fun getUsername(): String { + return user.name + } + + override fun isAccountNonExpired(): Boolean { + return true + } + + override fun isAccountNonLocked(): Boolean { + return true + } + + override fun isCredentialsNonExpired(): Boolean { + return true + } + + override fun isEnabled(): Boolean { + return true + } + + fun getProvider(): OAuthProvider { + return user.provider + } + fun getEmail():String{ + return user.email + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/PrincipalOauthUserServiceReactive.kt b/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/PrincipalOauthUserServiceReactive.kt new file mode 100644 index 0000000..dae616f --- /dev/null +++ b/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/PrincipalOauthUserServiceReactive.kt @@ -0,0 +1,103 @@ +package com.KY.KoreanYoutube.security_reactive + +import com.KY.KoreanYoutube.domain.UserR2dbc +import com.KY.KoreanYoutube.security.* +import com.KY.KoreanYoutube.user.UserR2DBCService +import org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2UserService +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest +import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService +import org.springframework.security.oauth2.core.OAuth2AuthenticationException +import org.springframework.security.oauth2.core.user.OAuth2User +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import reactor.core.publisher.Mono +import reactor.kotlin.core.publisher.switchIfEmpty + + +@Service +class PrincipalOauthUserServiceReactive( + private val userService: UserR2DBCService, +) : ReactiveOAuth2UserService { + + //구글로 부터 받은 userRequest 데이터에 대한 후처리되는 함수 + //함수 종료시 @AuthenticationPrincipal 어노테이션이 만들어진다. + @Throws(OAuth2AuthenticationException::class) + @Transactional(value = "transactionManager") + override fun loadUser(userRequest: OAuth2UserRequest): Mono { + //"registraionId" 로 어떤 OAuth 로 로그인 했는지 확인 가능(google,naver등) + //구글 로그인 버튼 클릭 -> 구글 로그인창 -> 로그인 완료 -> code를 리턴(OAuth-Clien라이브러리가 받아줌) -> code를 통해서 AcssToken요청(access토큰 받음) + // => "userRequest"가 감고 있는 정보 + //회원 프로필을 받아야하는데 여기서 사용되는것이 "loadUser" 함수이다 -> 구글 로 부터 회원 프로필을 받을수 있다. + /** + * OAuth 로그인 회원 가입 + */ + +// println("getClientRegistration: " + userRequest.clientRegistration) +// println("getAccessToken: " + userRequest.accessToken.tokenValue) +// println("getAttributes: " + super.loadUser(userRequest).attributes) + + + return oAuthToUser(userRequest) + + } + + private fun oAuthToUser(userRequest: OAuth2UserRequest): Mono { + val delegate = DefaultReactiveOAuth2UserService() + return delegate.loadUser(userRequest) + .doOnNext { + logger.info { "PRINCIPAL 지나감" } + } + .map { oAuth2User -> + var oAuth2UserInfo: OAuth2UserInfo? = null + when (userRequest.clientRegistration.registrationId) { + "google" -> { + oAuth2UserInfo = GoogleUserInfo(oAuth2User.attributes) + } + + "naver" -> { + oAuth2UserInfo = NaverUserInfo(oAuth2User.attributes["response"] as Map) + } + + "kakao" -> { + oAuth2UserInfo = KakaoUserInfo( + oAuth2User.attributes["kakao_account"] as Map, + oAuth2User.attributes["id"].toString() + ) + } + + else -> { + println("지원하지 않은 로그인 서비스 입니다.") + } + } + Pair(oAuth2UserInfo!!, oAuth2User.attributes) + } + .flatMap { p -> + Mono.zip(userInfo2User(p.first), Mono.just(p.second)) + } + .map { p -> + PrincipalDetailsReactive(p.t1, p.t2) + } + } + + private fun userInfo2User(oAuth2UserInfo : OAuth2UserInfo): Mono { + val provider: OAuthProvider = oAuth2UserInfo.provider + val providerId: String = oAuth2UserInfo.providerId + val password = providerId //중요하지 않음 그냥 패스워드 암호화 하 + val email: String = oAuth2UserInfo.email + val userId = provider.name + "_" + email + val role: Role = Role.USER + val name = oAuth2UserInfo.name + + return userService.findByUserId(userId).switchIfEmpty{ + val user = UserR2dbc( + name = name, + password = password, + email = email, + role = role, + provider = provider, + userId = userId + ) + userService.save(user) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/PrincipalServiceReactive.kt b/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/PrincipalServiceReactive.kt new file mode 100644 index 0000000..ea816fb --- /dev/null +++ b/src/main/kotlin/com/KY/KoreanYoutube/security_reactive/PrincipalServiceReactive.kt @@ -0,0 +1,38 @@ +package com.KY.KoreanYoutube.security_reactive + +import com.KY.KoreanYoutube.domain.User +import com.KY.KoreanYoutube.domain.UserR2dbc +import com.KY.KoreanYoutube.security.logger +import com.KY.KoreanYoutube.user.UserR2DBCService +import com.KY.KoreanYoutube.user.UserService +import lombok.RequiredArgsConstructor +import org.springframework.security.core.userdetails.ReactiveUserDetailsService +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.security.core.userdetails.UsernameNotFoundException +import org.springframework.stereotype.Service +import reactor.core.publisher.Mono + + +@Service +@RequiredArgsConstructor +class PrincipalServiceReactive( + private val userService: UserR2DBCService +) : ReactiveUserDetailsService { + + + //시큐리티 session => Authentication => UserDetails + // 여기서 리턴 된 값이 Authentication 안에 들어간다.(리턴될때 들어간다.) + // 그리고 시큐리티 session 안에 Authentication 이 들어간다. + //함수 종료시 @AuthenticationPrincipal 어노테이션이 만들어진다. + @Throws(UsernameNotFoundException::class) + override fun findByUsername(username: String): Mono { + return userService.findByUserId(username) + .doOnNext { + logger.info { "여기는 PrincipalServiceReactive" } + } + .map{ + return@map PrincipalDetailsReactive(it) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/user/UserR2DBCRepository.kt b/src/main/kotlin/com/KY/KoreanYoutube/user/UserR2DBCRepository.kt index b88be02..150591d 100644 --- a/src/main/kotlin/com/KY/KoreanYoutube/user/UserR2DBCRepository.kt +++ b/src/main/kotlin/com/KY/KoreanYoutube/user/UserR2DBCRepository.kt @@ -7,5 +7,5 @@ import reactor.core.publisher.Mono @Repository interface UserR2DBCRepository : R2dbcRepository { - fun findFirstByName(name: String): Mono + fun findFirstByUserId(userId: String): Mono } \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/user/UserR2DBCService.kt b/src/main/kotlin/com/KY/KoreanYoutube/user/UserR2DBCService.kt new file mode 100644 index 0000000..41d0c72 --- /dev/null +++ b/src/main/kotlin/com/KY/KoreanYoutube/user/UserR2DBCService.kt @@ -0,0 +1,21 @@ +package com.KY.KoreanYoutube.user + +import com.KY.KoreanYoutube.domain.User +import com.KY.KoreanYoutube.domain.UserR2dbc +import org.springframework.stereotype.Service +import reactor.core.publisher.Mono + + +@Service +class UserR2DBCService( + val userR2DBCRepository: UserR2DBCRepository +) { + + fun findByUserId(userId : String): Mono { + return userR2DBCRepository.findFirstByUserId(userId) + } + + fun save(user: UserR2dbc): Mono { + return userR2DBCRepository.save(user) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/KY/KoreanYoutube/video/VideoService.kt b/src/main/kotlin/com/KY/KoreanYoutube/video/VideoService.kt index 08efde6..fc63e93 100644 --- a/src/main/kotlin/com/KY/KoreanYoutube/video/VideoService.kt +++ b/src/main/kotlin/com/KY/KoreanYoutube/video/VideoService.kt @@ -4,6 +4,7 @@ import com.KY.KoreanYoutube.domain.VideoR2dbc import org.springframework.data.domain.Sort import org.springframework.stereotype.Service import reactor.core.publisher.Flux +import reactor.core.publisher.Mono @Service class VideoService( @@ -14,7 +15,7 @@ class VideoService( return videoR2DBCRepository.findAll(sort) } - fun findById(detailId: String): VideoR2dbc? { - return videoR2DBCRepository.findFirstByUrl(detailId).block() + fun findById(detailId: String): Mono { + return videoR2DBCRepository.findFirstByUrl(detailId) } } \ No newline at end of file