From cd1072430e3034674ea9638f92ae2554998f2216 Mon Sep 17 00:00:00 2001 From: lunaticbum Date: Wed, 4 Dec 2024 18:01:50 +0900 Subject: [PATCH] ... --- .../lunaticbum/back/lun/configs/AppConfig.kt | 7 +- .../back/lun/configs/BumsInterceptor.kt | 57 +++++++- .../back/lun/configs/GlobalEnvironment.kt | 9 +- .../back/lun/configs/JwtGenerator.kt | 69 +++++++++ .../back/lun/controllers/BlogController.kt | 10 +- .../back/lun/controllers/UserController.kt | 49 +++++-- .../back/lun/model/ResponceResult.kt | 1 + .../kr/lunaticbum/back/lun/model/TokenData.kt | 24 +++- .../kr/lunaticbum/back/lun/model/User.kt | 14 ++ .../lunaticbum/back/lun/service/JwtService.kt | 132 ++++++++++++++++++ .../kr/lunaticbum/back/lun/utils/JwtUtil.kt | 79 +++++++++++ src/main/resources/static/css/blog.css | 2 +- src/main/resources/static/css/common.css | 10 +- src/main/resources/static/js/common.js | 36 ++--- .../templates/content/blog/editor.html | 7 +- .../resources/templates/fragments/header.html | 9 +- .../templates/layout/default_layout.html | 9 -- 17 files changed, 465 insertions(+), 59 deletions(-) create mode 100644 src/main/kotlin/kr/lunaticbum/back/lun/configs/JwtGenerator.kt create mode 100644 src/main/kotlin/kr/lunaticbum/back/lun/service/JwtService.kt create mode 100644 src/main/kotlin/kr/lunaticbum/back/lun/utils/JwtUtil.kt diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/configs/AppConfig.kt b/src/main/kotlin/kr/lunaticbum/back/lun/configs/AppConfig.kt index c56cc60..6bc1783 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/configs/AppConfig.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/configs/AppConfig.kt @@ -1,6 +1,7 @@ package kr.lunaticbum.back.lun.configs import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.web.servlet.config.annotation.InterceptorRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurer @@ -14,12 +15,16 @@ class AppConfig : WebMvcConfigurer { @Value("\${resource.location}") private val resourceLocation: String? = null + @Bean + fun authInterceptor(): BumsInterceptor { + return BumsInterceptor() + } override fun addResourceHandlers(registry: org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry) { registry.addResourceHandler(resourceHandler).addResourceLocations(resourceLocation) } override fun addInterceptors(registry: InterceptorRegistry) { - registry.addInterceptor(BumsInterceptor()) + registry.addInterceptor(authInterceptor()) super.addInterceptors(registry) } // @Bean diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/configs/BumsInterceptor.kt b/src/main/kotlin/kr/lunaticbum/back/lun/configs/BumsInterceptor.kt index c16070d..2eca4d5 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/configs/BumsInterceptor.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/configs/BumsInterceptor.kt @@ -4,12 +4,24 @@ import com.google.gson.Gson import jakarta.servlet.http.Cookie import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse +import kr.lunaticbum.back.lun.service.JwtService +import org.springframework.beans.factory.annotation.Autowired import org.springframework.lang.Nullable +import org.springframework.stereotype.Component +import org.springframework.stereotype.Service import org.springframework.web.servlet.HandlerInterceptor import org.springframework.web.servlet.ModelAndView +@Component +class BumsInterceptor : HandlerInterceptor { + + @Autowired + lateinit var jwtService : JwtService + @Autowired + lateinit var globalEvv : GlobalEnvironment + + val WRITE_PERMISSION_KEY = "PERMISSION" -class BumsInterceptor : HandlerInterceptor{ @Throws(Exception::class) override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { println("===============================================") @@ -17,7 +29,8 @@ class BumsInterceptor : HandlerInterceptor{ println("Request URL ===> " + request.requestURL) return super.preHandle(request, response, handler) } - val WRITE_PERMISSION_KEY = "PERMISSION" + + @Throws(Exception::class) override fun postHandle( request: HttpServletRequest, @@ -25,12 +38,37 @@ class BumsInterceptor : HandlerInterceptor{ handler: Any, @Nullable modelAndView: ModelAndView? ) { - if (request.requestURI.contains("logout") == false && !request.cookies.isNullOrEmpty() && request.cookies.filter { it.name.equals("S33-DATA") && it.value.length > 0 }.size > 0) { - response.addCookie(Cookie("S33-DATA",request.cookies.filter { it.name.equals("S33-DATA") && it.value.length > 0 }.get(0).value)) - modelAndView?.modelMap?.put(WRITE_PERMISSION_KEY,"OK") + if (request.requestURI.contains("logout") == false && !request.cookies.isNullOrEmpty() && request.cookies.filter { it.name.equals("access") && it.value.length > 0 }.size > 0) { + var correctUserCheck = -1; + var access : Cookie?= null + var refresh : Cookie?= null + request.cookies.forEach { + if (it.name.equals("access", true) && jwtService.validateAccessToken(it.value)){ + access = it + correctUserCheck += 1 + println("Response access correctUserCheck ===> ${correctUserCheck}") + } + } + request.cookies.forEach { + if (it.name.equals("refresh", true) && jwtService.validateRefreshToken(access?.value,it.value)){ + refresh = it + correctUserCheck += 1 + println("Response refresh correctUserCheck ===> ${correctUserCheck}") + } + } + if (correctUserCheck > 0) { + println("Response correctUserCheck ===> ${correctUserCheck}") + response.addCookie(cookieUpdate(refresh)) + response.addCookie(cookieUpdate(access)) + modelAndView?.modelMap?.put(WRITE_PERMISSION_KEY,"OK") + } else { + println("Response correctUserCheck ===> ${correctUserCheck}") + response.addCookie(Cookie("access","").apply { maxAge = -1 }) + response.addCookie(Cookie("refresh","").apply { maxAge = -1 }) + modelAndView?.modelMap?.put(WRITE_PERMISSION_KEY,"NO") + } println("Response modelMap ===> ${Gson().toJson(modelAndView?.modelMap)}") } else if (request.requestURI.contains("logout")) { - response.addCookie(Cookie("S33-DATA",null)) modelAndView?.modelMap?.put(WRITE_PERMISSION_KEY,"NO") } @@ -39,5 +77,10 @@ class BumsInterceptor : HandlerInterceptor{ super.postHandle(request, response, handler, modelAndView) } - + fun cookieUpdate(cookie: Cookie?) : Cookie? { + cookie?.maxAge = (globalEvv.ACCESS_EXPIRATION / 1000).toInt() + cookie?.domain = "lunaticbum.kr" + cookie?.secure = true + return cookie + } } \ No newline at end of file diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/configs/GlobalEnvironment.kt b/src/main/kotlin/kr/lunaticbum/back/lun/configs/GlobalEnvironment.kt index f1185c5..cde5d9f 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/configs/GlobalEnvironment.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/configs/GlobalEnvironment.kt @@ -29,7 +29,14 @@ class GlobalEnvironment : EnvironmentAware { @Value("\${weather.api.key}") var weatherApiKey: String? = "" - +// @Value("jwt.access-secret") + var ACCESS_SECRET_KEY: String = "l00u00n00a00t00i00c00b00u00m00a00c00sk" +// @Value("jwt.refresh-secret") + var REFRESH_SECRET_KEY: String = "l00u00n00a00t00i00c00b00u00m00r00f00sk" +// @Value("jwt.access-expiration") + var ACCESS_EXPIRATION: Long = 60 * 5 * 1000L +// @Value("jwt.refresh-expiration") + var REFRESH_EXPIRATION: Long = 60 * 5 * 1000L override fun setEnvironment(environment: Environment) { println ("telegramBotKey $telegramBotKey") diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/configs/JwtGenerator.kt b/src/main/kotlin/kr/lunaticbum/back/lun/configs/JwtGenerator.kt new file mode 100644 index 0000000..7be2632 --- /dev/null +++ b/src/main/kotlin/kr/lunaticbum/back/lun/configs/JwtGenerator.kt @@ -0,0 +1,69 @@ +package kr.lunaticbum.back.lun.configs + +import io.jsonwebtoken.Jwts +import io.jsonwebtoken.SignatureAlgorithm +import kr.lunaticbum.back.lun.model.User +import lombok.Getter +import lombok.RequiredArgsConstructor +import org.springframework.stereotype.Component +import java.security.Key +import java.util.* +import kotlin.collections.HashMap + + +@Component +class JwtGenerator { + fun generateAccessToken(ACCESS_SECRET: Key?, ACCESS_EXPIRATION: Long, user: User): String { + val now = System.currentTimeMillis() + + return Jwts.builder() + .setHeader(createHeader()) + .setClaims(createClaims(user)) + .setSubject(user.userId) + .setExpiration(Date(now + ACCESS_EXPIRATION)) + .signWith(ACCESS_SECRET, SignatureAlgorithm.HS256) + .compact() + } + + fun generateRefreshToken(REFRESH_SECRET: Key?, REFRESH_EXPIRATION: Long, user: User): String { + val now = System.currentTimeMillis() + + return Jwts.builder() + .setHeader(createHeader()) + .setSubject(user.getIdentifier()) + .setExpiration(Date(now + REFRESH_EXPIRATION)) + .signWith(REFRESH_SECRET, SignatureAlgorithm.HS256) + .compact() + } + + private fun createHeader(): Map { + val header: MutableMap = HashMap() + header["typ"] = "JWT" + header["alg"] = "HS256" + return header + } + + private fun createClaims(user: User): Map { + val claims: MutableMap = HashMap() + claims["Identifier"] = user.getIdentifier() + claims["Role"] = user.getRole() + return claims + } +} + +@RequiredArgsConstructor +@Getter +enum class TokenStatus { + AUTHENTICATED, + EXPIRED, + INVALID +} + +@RequiredArgsConstructor +@Getter +enum class JwtRule(val value: String) { + JWT_ISSUE_HEADER("Set-Cookie"), + JWT_RESOLVE_HEADER("Cookie"), + ACCESS_PREFIX("access"), + REFRESH_PREFIX("refresh"); +} \ No newline at end of file diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/controllers/BlogController.kt b/src/main/kotlin/kr/lunaticbum/back/lun/controllers/BlogController.kt index 5e89b3f..efd78ab 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/controllers/BlogController.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/controllers/BlogController.kt @@ -136,10 +136,16 @@ class BlogController() { } @GetMapping("modify") - fun modify(@RequestParam("token") token : String?) : ResultMV{ + fun modify(httpServletRequest: HttpServletRequest,@RequestParam("token") token : String?) : ResultMV{ logService.log("incoming modify") val vm = ResultMV("content/blog/modify") - if (TEMPTOKEN.equals(token)) { + var s33Key : String? = null + if (!httpServletRequest.cookies.isNullOrEmpty()) { + httpServletRequest.cookies.forEach { if (it.name.equals("S33-DATA")){ + s33Key = it.value + } } + } + if (TEMPTOKEN.equals(token)|| s33Key?.length ?: 0 > 5) { postManageg.find20()?.apply { forEach { it.title = URLDecoder.decode(it.title) diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/controllers/UserController.kt b/src/main/kotlin/kr/lunaticbum/back/lun/controllers/UserController.kt index e935a20..211177a 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/controllers/UserController.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/controllers/UserController.kt @@ -2,15 +2,20 @@ package kr.lunaticbum.back.lun.controllers import com.google.gson.Gson import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse import kr.lunaticbum.back.lun.configs.GlobalEnvironment import kr.lunaticbum.back.lun.configs.GlobalEnvironment.Companion.ApiKeyWordKey import kr.lunaticbum.back.lun.configs.GlobalEnvironment.Companion.EncType11 import kr.lunaticbum.back.lun.configs.GlobalEnvironment.Companion.EncTypeKey +import kr.lunaticbum.back.lun.configs.JwtRule import kr.lunaticbum.back.lun.model.* +import kr.lunaticbum.back.lun.service.JwtService +import kr.lunaticbum.back.lun.utils.JwtUtil import kr.lunaticbum.back.lun.utils.LogService import kr.lunaticbum.back.lun.utils.extractModelData import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.MediaType +import org.springframework.http.ResponseCookie import org.springframework.http.ResponseEntity import org.springframework.security.core.userdetails.UserDetails import org.springframework.web.bind.annotation.* @@ -33,6 +38,8 @@ class UserController { @Autowired lateinit var userManager: UserManager + @Autowired + lateinit var jwtService : JwtService @GetMapping("join") fun hello(httpServletRequest: HttpServletRequest): ResultMV { @@ -57,12 +64,6 @@ class UserController { fun userLogin(httpServletRequest: HttpServletRequest): ResultMV { logService.log("onJoin") val vm = ResultMV("content/user/login") -// when(System.currentTimeMillis() % 5L) { -// 0L -> vm.modelMap.put(EncTypeKey,"T4") -// 1L -> vm.modelMap.put(EncTypeKey,"T3") -// 2L -> vm.modelMap.put(EncTypeKey,"T2") -// else -> vm.modelMap.put(EncTypeKey,"T0") -// } vm.modelMap.put(EncTypeKey,EncType11) vm.modelMap.put(ApiKeyWordKey,"LOGIN") return vm @@ -71,23 +72,25 @@ class UserController { @ResponseBody @PostMapping("login.ajax") - fun login(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity { + fun login(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity { logService.log(httpServletRequest.requestURI) logService.log(jsonString) var lResultCode = 0 var lResultMsg = "Suscces" var u : UserDetails? = null + var user : User? = null + var tokenData : TokenData? = null jsonString.extractModelData { exception, originDataString -> if (exception == null) { logService.log(originDataString) val target = Gson().fromJson(originDataString, User::class.java) ?: User() - var user = userManager.findById(target.user_id!!)?.block() + user = userManager.findById(target.user_id!!)?.block() if (user == null && ((target.user_id?.length ?: 0) > 3 == true)) { user = userManager.findByEmail(target.user_id!!)?.block() } if (user != null) { - if(userManager.isCorrectUser(user,target.user_pw!!)){ - + if(userManager.isCorrectUser(user!!,target.user_pw!!)){ + tokenData = jwtService.generate(user!!) } else { lResultMsg = "is wrong infomation id or passord" lResultCode = 7100 @@ -103,14 +106,34 @@ class UserController { } } val responce = ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).headers { - it.put("S33-DATA", listOf(UUID.randomUUID().toString())) - }.body(ResponceResult().apply { + + + + + }.body(LoginResult().apply { this.resultCode = lResultCode this.resultMsg = lResultMsg - }) + this.token = setTokenToCookie(JwtRule.ACCESS_PREFIX.value, tokenData?.tokenKey ?: "", globalEvv.ACCESS_EXPIRATION / 1000).toString().replace("access=","") + this.refresh = setTokenToCookie(JwtRule.REFRESH_PREFIX.value, tokenData?.refreshToken ?: "", globalEvv.REFRESH_EXPIRATION / 1000).toString().replace("refresh=","") + }).apply { + + + } + return responce } + + private fun setTokenToCookie(tokenPrefix: String, token: String, maxAgeSeconds: Long): ResponseCookie { + return ResponseCookie.from(tokenPrefix, token) + .path("/") + .maxAge(maxAgeSeconds) + .httpOnly(true) + .sameSite("None") + .secure(true) + .build() + } + @ResponseBody @PostMapping("logout.ajax") fun logout(httpServletRequest: HttpServletRequest) : ResponseEntity { diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/model/ResponceResult.kt b/src/main/kotlin/kr/lunaticbum/back/lun/model/ResponceResult.kt index 5cad54e..c066272 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/model/ResponceResult.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/model/ResponceResult.kt @@ -12,6 +12,7 @@ open class ResponceResult : BaseResult() { @Getter open class LoginResult : ResponceResult() { var token: String? = null + var refresh: String? = null } @Getter diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/model/TokenData.kt b/src/main/kotlin/kr/lunaticbum/back/lun/model/TokenData.kt index 35a99bd..9fcc123 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/model/TokenData.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/model/TokenData.kt @@ -6,17 +6,37 @@ import lombok.NoArgsConstructor import org.springframework.data.annotation.Id import org.springframework.data.mongodb.core.index.Indexed import org.springframework.data.mongodb.core.mapping.Document +import org.springframework.data.mongodb.repository.ReactiveMongoRepository +import org.springframework.stereotype.Repository +import reactor.core.publisher.Mono +import java.time.LocalDateTime import java.util.* + @Data @NoArgsConstructor @AllArgsConstructor @Document(collection = "TokenData") class TokenData { - @Indexed(expireAfterSeconds = 60 * 5) - var expireAt: Date? = null + @Indexed(name = "ttl",expireAfterSeconds = 0) + var expireAt: LocalDateTime? = null + @Id var tokenKey : String? = null + var refreshToken : String? = null + constructor(tokenKey: String?, refreshToken: String?) { + this.expireAt = LocalDateTime.now().plusSeconds(300) + this.tokenKey = tokenKey + this.refreshToken = refreshToken + + } +} + + +@Repository +interface TokenDataRepository : ReactiveMongoRepository { + fun findBytokenKey(tokenKey: String): Mono + fun deleteBytokenKey(tokenKey: String) } \ No newline at end of file diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/model/User.kt b/src/main/kotlin/kr/lunaticbum/back/lun/model/User.kt index 957a6d2..5ee7d97 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/model/User.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/model/User.kt @@ -84,6 +84,16 @@ class User { fun checkPassword(plainPassword: String?, passwordEncoder: PasswordEncoder): Boolean { return passwordEncoder.matches(plainPassword, this.user_pw) } + + fun getIdentifier(): String? { + return userId + } + + fun getRole(): UserRole { + if ("Y".equals(isAdmin)) return UserRole.ADMIN + if ("Y".equals(isAccept)) return UserRole.WRITE + return UserRole.READ + } } @Getter @@ -107,6 +117,10 @@ interface UserRepository : ReactiveMongoRepository { fun save(user: User): Mono } + +enum class UserRole { + NOT_REGISTERED,READ,WRITE,ADMIN +} interface UserService { fun findById(id: String): Mono? fun findByEmail(id: String): Mono? diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/service/JwtService.kt b/src/main/kotlin/kr/lunaticbum/back/lun/service/JwtService.kt new file mode 100644 index 0000000..09c71d2 --- /dev/null +++ b/src/main/kotlin/kr/lunaticbum/back/lun/service/JwtService.kt @@ -0,0 +1,132 @@ +package kr.lunaticbum.back.lun.service + +import io.jsonwebtoken.Jwts +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import kr.lunaticbum.back.lun.configs.GlobalEnvironment +import kr.lunaticbum.back.lun.configs.JwtGenerator +import kr.lunaticbum.back.lun.configs.JwtRule +import kr.lunaticbum.back.lun.configs.TokenStatus +import kr.lunaticbum.back.lun.model.* +import kr.lunaticbum.back.lun.utils.BusinessException +import kr.lunaticbum.back.lun.utils.ErrorCode +import kr.lunaticbum.back.lun.utils.JwtUtil +import lombok.extern.slf4j.Slf4j +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.http.HttpHeaders +import org.springframework.http.ResponseCookie +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.security.core.Authentication +import org.springframework.security.core.token.Token +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import java.security.Key +import java.time.Duration +import java.util.* +import java.util.function.Consumer + + +@Service +@Transactional(readOnly = true) +@Slf4j +class JwtService { + + @Autowired + lateinit var globalEvv : GlobalEnvironment + + @Autowired + private lateinit var jwtGenerator: JwtGenerator + + @Autowired + private lateinit var jwtUtil: JwtUtil + + + @Autowired + private lateinit var customUserDetailsService: UserManager + + @Autowired + private lateinit var tokenRepository : TokenDataRepository + +// private val ACCESS_SECRET_KEY: Key = jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY) +// private val REFRESH_SECRET_KEY: Key = jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY) + + fun validateUser(requestUser: User) { + if (requestUser.getRole() === UserRole.NOT_REGISTERED) { + throw BusinessException(ErrorCode.NOT_AUTHENTICATED_USER) + } + } + + @Transactional + fun generate(requestUser: User): TokenData? { + var accessToken = jwtGenerator.generateAccessToken(jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY), globalEvv.ACCESS_EXPIRATION, requestUser) + var refreshToken = jwtGenerator.generateRefreshToken(jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY), globalEvv.REFRESH_EXPIRATION, requestUser) + var token = TokenData(accessToken, refreshToken) + return tokenRepository.save(token).block() + } + + + + private fun setTokenToCookie(tokenPrefix: String, token: String, maxAgeSeconds: Long): ResponseCookie { + return ResponseCookie.from(tokenPrefix, token) + .path("/") + .maxAge(maxAgeSeconds) + .httpOnly(true) + .sameSite("None") + .secure(true) + .build() + } + + fun validateAccessToken(token: String?): Boolean { + return jwtUtil.getTokenStatus(token, jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY)) == TokenStatus.AUTHENTICATED + } + + fun validateRefreshToken(token: String?, refreshToken: String?): Boolean { + val isRefreshValid = jwtUtil.getTokenStatus(refreshToken, jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY)) == TokenStatus.AUTHENTICATED + val storedToken: TokenData? = tokenRepository.findBytokenKey(token ?: "").block(Duration.ofSeconds(10)) + val isTokenMatched: Boolean = storedToken?.refreshToken.equals(refreshToken) + return isRefreshValid && isTokenMatched + } + + fun resolveTokenFromCookie(request: HttpServletRequest, tokenPrefix: JwtRule?): String { + val cookies = request.cookies ?: throw BusinessException(ErrorCode.JWT_TOKEN_NOT_FOUND) + return jwtUtil.resolveTokenFromCookie(cookies, tokenPrefix!!) + } + + fun getAuthentication(token: String): Authentication { + val principal: UserDetails = customUserDetailsService.loadUserByUsername(getUserPk(token, jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY))) + return UsernamePasswordAuthenticationToken(principal, "", principal.authorities) + } + + private fun getUserPk(token: String, secretKey: Key): String { + return Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(token) + .getBody() + .getSubject() + } + + fun getIdentifierFromRefresh(refreshToken: String?): String { + try { + return Jwts.parserBuilder() + .setSigningKey(jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY)) + .build() + .parseClaimsJws(refreshToken) + .getBody() + .getSubject() + } catch (e: Exception) { + throw BusinessException(ErrorCode.INVALID_JWT) + } + } + + fun logout(token: String, response: HttpServletResponse) { + tokenRepository.deleteBytokenKey(token) + + val accessCookie = jwtUtil.resetToken(JwtRule.ACCESS_PREFIX) + val refreshCookie = jwtUtil.resetToken(JwtRule.REFRESH_PREFIX) + + response.addCookie(accessCookie) + response.addCookie(refreshCookie) + } +} \ No newline at end of file diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/utils/JwtUtil.kt b/src/main/kotlin/kr/lunaticbum/back/lun/utils/JwtUtil.kt new file mode 100644 index 0000000..b9cd799 --- /dev/null +++ b/src/main/kotlin/kr/lunaticbum/back/lun/utils/JwtUtil.kt @@ -0,0 +1,79 @@ +package kr.lunaticbum.back.lun.utils + +import io.jsonwebtoken.ExpiredJwtException +import io.jsonwebtoken.JwtException +import io.jsonwebtoken.Jwts +import io.jsonwebtoken.security.Keys +import jakarta.servlet.http.Cookie +import kr.lunaticbum.back.lun.configs.JwtRule +import kr.lunaticbum.back.lun.configs.TokenStatus +import lombok.RequiredArgsConstructor +import lombok.extern.slf4j.Slf4j +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import java.nio.charset.StandardCharsets +import java.security.Key +import java.util.* + + +@Slf4j +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +class JwtUtil { + + + fun getTokenStatus(token: String?, secretKey: Key?): TokenStatus { + try { + var cls = Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(token) + cls.body.keys.forEach { + println("${it} >>> ${cls.body.get(it).toString()}") + } + return TokenStatus.AUTHENTICATED + } catch (e: ExpiredJwtException) { +// log.error(INVALID_EXPIRED_JWT.getMessage()) + return TokenStatus.EXPIRED + } catch (e: IllegalArgumentException) { +// log.error(INVALID_EXPIRED_JWT.getMessage()) + return TokenStatus.EXPIRED + } catch (e: JwtException) { + throw BusinessException(ErrorCode.INVALID_JWT) + } + } + + fun resolveTokenFromCookie(cookies: Array?, tokenPrefix: JwtRule): String { + return Arrays.stream(cookies) + .filter { cookie -> cookie.getName().equals(tokenPrefix.value, ignoreCase = true) } + .findFirst() + .map { it.value } + .orElse("") + } + + fun getSigningKey(secretKey: String): Key { + val encodedKey = encodeToBase64(secretKey) + return Keys.hmacShaKeyFor(encodedKey.toByteArray(StandardCharsets.UTF_8)) + } + + private fun encodeToBase64(secretKey: String): String { + return Base64.getEncoder().encodeToString(secretKey.toByteArray()) + } + + fun resetToken(tokenPrefix: JwtRule): Cookie { + val cookie: Cookie = Cookie(tokenPrefix.value, null) + cookie.setMaxAge(0) + cookie.setPath("/") + return cookie + } +} +class BusinessException(error : ErrorCode) : Exception(error.name) + +enum class ErrorCode { + JWT_TOKEN_NOT_FOUND, + NOT_AUTHENTICATED_USER, + INVALID_EXPIRED_JWT, + INVALID_JWT +} \ No newline at end of file diff --git a/src/main/resources/static/css/blog.css b/src/main/resources/static/css/blog.css index cda216b..149afe2 100644 --- a/src/main/resources/static/css/blog.css +++ b/src/main/resources/static/css/blog.css @@ -163,4 +163,4 @@ a.btn_layerClose:hover { padding: 1px; border-radius: 10px; background: #00000044; -} \ No newline at end of file +} diff --git a/src/main/resources/static/css/common.css b/src/main/resources/static/css/common.css index 2498fb5..9e6dbcd 100644 --- a/src/main/resources/static/css/common.css +++ b/src/main/resources/static/css/common.css @@ -43,6 +43,12 @@ body > *{ /*}*/ +.center_menu { + + display: inline-flex; + justify-content: space-evenly; +} + header { top: 0; /*background: var(--DEFAULT_LAYER_BACK);*/ @@ -51,8 +57,10 @@ header { border-width: 1px; height: 5vh; min-height: 5vh; + grid-auto-flow: column; display: flex;; position: relative; + justify-content: space-around; } .user_info { @@ -95,7 +103,7 @@ header { } #top { - float: left; + display: inline-block; justify-content: space-between; margin-left: auto; diff --git a/src/main/resources/static/js/common.js b/src/main/resources/static/js/common.js index a652dbb..f3f5cec 100644 --- a/src/main/resources/static/js/common.js +++ b/src/main/resources/static/js/common.js @@ -81,12 +81,6 @@ function postLogin(target,type, data, key,callBackResult) { if (httpRequest.readyState === XMLHttpRequest.DONE) { if (httpRequest.status === 200) { try { - var uuid= httpRequest.getResponseHeader("S33-DATA") - console.log("LOG __ 0" + document.cookie) - console.log("LOG __ 1" + uuid) - document.cookie = "S33-DATA="+uuid - console.log("LOG __ 2" + document.cookie) - console.log("LOG __ 3" + uuid) callBackResult(httpRequest.response) document.location.href = document.location } catch (e) { @@ -121,19 +115,22 @@ function mainPath() { } } +function gotoWrite() { + document.location.href = getMainPath()+"/blog/write" +} + +function gotoModify() { + document.location.href = getMainPath()+"/blog/modify" +} + +function gotoWhere() { + document.location.href = getMainPath()+"/bums/where" +} function logout() { - if(document.cookie.split(";").length > 1) { - document.cookie.split(";").forEach(function (v,i,a){ - if(v.search("S33-DATA") > 0) { - document.cookie.replace(v,"S33-DATA=") - } else { - - } - }) - } else { - document.cookie = "S33-DATA=" - } + // retrieve all cookies + document.cookie = "access=; expires=Thu, 01 Jan 1970 00:00:01 GMT;" + document.cookie = "refresh=; expires=Thu, 01 Jan 1970 00:00:01 GMT;" let logOutUrl = getMainPath() + "/user/logout.ajax"; post(logOutUrl,"","","", function (resultData) { alert(resultData) @@ -159,7 +156,10 @@ function onclickLogin(type, keyword) { 'user_pw': user_pw.value, } postLogin(getMainPath()+"/user/login.ajax",type,JSON.stringify(data),keyword, function (resultData) { - alert(resultData) + var data = JSON.parse(resultData) + // alert(resultData) + document.cookie = "access=" + data.token.split(";")[0]+";" + document.cookie = "refresh=" + data.refresh.split(";")[0]+";" }) } diff --git a/src/main/resources/templates/content/blog/editor.html b/src/main/resources/templates/content/blog/editor.html index 0be5ff1..a50e786 100644 --- a/src/main/resources/templates/content/blog/editor.html +++ b/src/main/resources/templates/content/blog/editor.html @@ -28,6 +28,7 @@ console.log(window.c) console.log(style.getPropertyValue('--FooterHeight')) console.log(style.getPropertyValue('--TopHeight')) + document.querySelector("#title_field").value = baseData.title var editorHeght = ( document.querySelector('#main_layer').getBoundingClientRect().height - ( @@ -88,9 +89,9 @@

권한이 없는 뎁쇼?!

-
- -
+ + +
diff --git a/src/main/resources/templates/fragments/header.html b/src/main/resources/templates/fragments/header.html index c052397..5fd3d17 100644 --- a/src/main/resources/templates/fragments/header.html +++ b/src/main/resources/templates/fragments/header.html @@ -2,10 +2,10 @@
+ -