This commit is contained in:
lunaticbum 2024-12-04 18:01:50 +09:00
parent 576932da3d
commit cd1072430e
17 changed files with 465 additions and 59 deletions

View File

@ -1,6 +1,7 @@
package kr.lunaticbum.back.lun.configs package kr.lunaticbum.back.lun.configs
import org.springframework.beans.factory.annotation.Value import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.InterceptorRegistry import org.springframework.web.servlet.config.annotation.InterceptorRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@ -14,12 +15,16 @@ class AppConfig : WebMvcConfigurer {
@Value("\${resource.location}") @Value("\${resource.location}")
private val resourceLocation: String? = null private val resourceLocation: String? = null
@Bean
fun authInterceptor(): BumsInterceptor {
return BumsInterceptor()
}
override fun addResourceHandlers(registry: org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry) { override fun addResourceHandlers(registry: org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry) {
registry.addResourceHandler(resourceHandler).addResourceLocations(resourceLocation) registry.addResourceHandler(resourceHandler).addResourceLocations(resourceLocation)
} }
override fun addInterceptors(registry: InterceptorRegistry) { override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(BumsInterceptor()) registry.addInterceptor(authInterceptor())
super.addInterceptors(registry) super.addInterceptors(registry)
} }
// @Bean // @Bean

View File

@ -4,12 +4,24 @@ import com.google.gson.Gson
import jakarta.servlet.http.Cookie import jakarta.servlet.http.Cookie
import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse 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.lang.Nullable
import org.springframework.stereotype.Component
import org.springframework.stereotype.Service
import org.springframework.web.servlet.HandlerInterceptor import org.springframework.web.servlet.HandlerInterceptor
import org.springframework.web.servlet.ModelAndView 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) @Throws(Exception::class)
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
println("===============================================") println("===============================================")
@ -17,7 +29,8 @@ class BumsInterceptor : HandlerInterceptor{
println("Request URL ===> " + request.requestURL) println("Request URL ===> " + request.requestURL)
return super.preHandle(request, response, handler) return super.preHandle(request, response, handler)
} }
val WRITE_PERMISSION_KEY = "PERMISSION"
@Throws(Exception::class) @Throws(Exception::class)
override fun postHandle( override fun postHandle(
request: HttpServletRequest, request: HttpServletRequest,
@ -25,12 +38,37 @@ class BumsInterceptor : HandlerInterceptor{
handler: Any, handler: Any,
@Nullable modelAndView: ModelAndView? @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) { if (request.requestURI.contains("logout") == false && !request.cookies.isNullOrEmpty() && request.cookies.filter { it.name.equals("access") && 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)) var correctUserCheck = -1;
modelAndView?.modelMap?.put(WRITE_PERMISSION_KEY,"OK") 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)}") println("Response modelMap ===> ${Gson().toJson(modelAndView?.modelMap)}")
} else if (request.requestURI.contains("logout")) { } else if (request.requestURI.contains("logout")) {
response.addCookie(Cookie("S33-DATA",null))
modelAndView?.modelMap?.put(WRITE_PERMISSION_KEY,"NO") modelAndView?.modelMap?.put(WRITE_PERMISSION_KEY,"NO")
} }
@ -39,5 +77,10 @@ class BumsInterceptor : HandlerInterceptor{
super.postHandle(request, response, handler, modelAndView) 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
}
} }

View File

@ -29,7 +29,14 @@ class GlobalEnvironment : EnvironmentAware {
@Value("\${weather.api.key}") @Value("\${weather.api.key}")
var weatherApiKey: String? = "" 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) { override fun setEnvironment(environment: Environment) {
println ("telegramBotKey $telegramBotKey") println ("telegramBotKey $telegramBotKey")

View File

@ -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<String, Any> {
val header: MutableMap<String, Any> = HashMap()
header["typ"] = "JWT"
header["alg"] = "HS256"
return header
}
private fun createClaims(user: User): Map<String, Any?> {
val claims: MutableMap<String, Any?> = 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");
}

View File

@ -136,10 +136,16 @@ class BlogController() {
} }
@GetMapping("modify") @GetMapping("modify")
fun modify(@RequestParam("token") token : String?) : ResultMV{ fun modify(httpServletRequest: HttpServletRequest,@RequestParam("token") token : String?) : ResultMV{
logService.log("incoming modify") logService.log("incoming modify")
val vm = ResultMV("content/blog/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 { postManageg.find20()?.apply {
forEach { forEach {
it.title = URLDecoder.decode(it.title) it.title = URLDecoder.decode(it.title)

View File

@ -2,15 +2,20 @@ package kr.lunaticbum.back.lun.controllers
import com.google.gson.Gson import com.google.gson.Gson
import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import kr.lunaticbum.back.lun.configs.GlobalEnvironment import kr.lunaticbum.back.lun.configs.GlobalEnvironment
import kr.lunaticbum.back.lun.configs.GlobalEnvironment.Companion.ApiKeyWordKey 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.EncType11
import kr.lunaticbum.back.lun.configs.GlobalEnvironment.Companion.EncTypeKey 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.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.LogService
import kr.lunaticbum.back.lun.utils.extractModelData import kr.lunaticbum.back.lun.utils.extractModelData
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.http.ResponseCookie
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.security.core.userdetails.UserDetails import org.springframework.security.core.userdetails.UserDetails
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
@ -33,6 +38,8 @@ class UserController {
@Autowired @Autowired
lateinit var userManager: UserManager lateinit var userManager: UserManager
@Autowired
lateinit var jwtService : JwtService
@GetMapping("join") @GetMapping("join")
fun hello(httpServletRequest: HttpServletRequest): ResultMV { fun hello(httpServletRequest: HttpServletRequest): ResultMV {
@ -57,12 +64,6 @@ class UserController {
fun userLogin(httpServletRequest: HttpServletRequest): ResultMV { fun userLogin(httpServletRequest: HttpServletRequest): ResultMV {
logService.log("onJoin") logService.log("onJoin")
val vm = ResultMV("content/user/login") 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(EncTypeKey,EncType11)
vm.modelMap.put(ApiKeyWordKey,"LOGIN") vm.modelMap.put(ApiKeyWordKey,"LOGIN")
return vm return vm
@ -71,23 +72,25 @@ class UserController {
@ResponseBody @ResponseBody
@PostMapping("login.ajax") @PostMapping("login.ajax")
fun login(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity<ResponceResult> { fun login(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity<LoginResult> {
logService.log(httpServletRequest.requestURI) logService.log(httpServletRequest.requestURI)
logService.log(jsonString) logService.log(jsonString)
var lResultCode = 0 var lResultCode = 0
var lResultMsg = "Suscces" var lResultMsg = "Suscces"
var u : UserDetails? = null var u : UserDetails? = null
var user : User? = null
var tokenData : TokenData? = null
jsonString.extractModelData { exception, originDataString -> jsonString.extractModelData { exception, originDataString ->
if (exception == null) { if (exception == null) {
logService.log(originDataString) logService.log(originDataString)
val target = Gson().fromJson(originDataString, User::class.java) ?: User() 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)) { if (user == null && ((target.user_id?.length ?: 0) > 3 == true)) {
user = userManager.findByEmail(target.user_id!!)?.block() user = userManager.findByEmail(target.user_id!!)?.block()
} }
if (user != null) { if (user != null) {
if(userManager.isCorrectUser(user,target.user_pw!!)){ if(userManager.isCorrectUser(user!!,target.user_pw!!)){
tokenData = jwtService.generate(user!!)
} else { } else {
lResultMsg = "is wrong infomation id or passord" lResultMsg = "is wrong infomation id or passord"
lResultCode = 7100 lResultCode = 7100
@ -103,14 +106,34 @@ class UserController {
} }
} }
val responce = ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).headers { 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.resultCode = lResultCode
this.resultMsg = lResultMsg 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 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 @ResponseBody
@PostMapping("logout.ajax") @PostMapping("logout.ajax")
fun logout(httpServletRequest: HttpServletRequest) : ResponseEntity<ResponceResult> { fun logout(httpServletRequest: HttpServletRequest) : ResponseEntity<ResponceResult> {

View File

@ -12,6 +12,7 @@ open class ResponceResult : BaseResult() {
@Getter @Getter
open class LoginResult : ResponceResult() { open class LoginResult : ResponceResult() {
var token: String? = null var token: String? = null
var refresh: String? = null
} }
@Getter @Getter

View File

@ -6,17 +6,37 @@ import lombok.NoArgsConstructor
import org.springframework.data.annotation.Id import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.index.Indexed import org.springframework.data.mongodb.core.index.Indexed
import org.springframework.data.mongodb.core.mapping.Document 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.* import java.util.*
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Document(collection = "TokenData") @Document(collection = "TokenData")
class TokenData { class TokenData {
@Indexed(expireAfterSeconds = 60 * 5) @Indexed(name = "ttl",expireAfterSeconds = 0)
var expireAt: Date? = null var expireAt: LocalDateTime? = null
@Id @Id
var tokenKey : String? = null var tokenKey : String? = null
var refreshToken : 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<TokenData, String> {
fun findBytokenKey(tokenKey: String): Mono<TokenData>
fun deleteBytokenKey(tokenKey: String)
} }

View File

@ -84,6 +84,16 @@ class User {
fun checkPassword(plainPassword: String?, passwordEncoder: PasswordEncoder): Boolean { fun checkPassword(plainPassword: String?, passwordEncoder: PasswordEncoder): Boolean {
return passwordEncoder.matches(plainPassword, this.user_pw) 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 @Getter
@ -107,6 +117,10 @@ interface UserRepository : ReactiveMongoRepository<User, String> {
fun save(user: User): Mono<User> fun save(user: User): Mono<User>
} }
enum class UserRole {
NOT_REGISTERED,READ,WRITE,ADMIN
}
interface UserService { interface UserService {
fun findById(id: String): Mono<User>? fun findById(id: String): Mono<User>?
fun findByEmail(id: String): Mono<User>? fun findByEmail(id: String): Mono<User>?

View File

@ -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)
}
}

View File

@ -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<Cookie>?, 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
}

View File

@ -43,6 +43,12 @@ body > *{
/*}*/ /*}*/
.center_menu {
display: inline-flex;
justify-content: space-evenly;
}
header { header {
top: 0; top: 0;
/*background: var(--DEFAULT_LAYER_BACK);*/ /*background: var(--DEFAULT_LAYER_BACK);*/
@ -51,8 +57,10 @@ header {
border-width: 1px; border-width: 1px;
height: 5vh; height: 5vh;
min-height: 5vh; min-height: 5vh;
grid-auto-flow: column;
display: flex;; display: flex;;
position: relative; position: relative;
justify-content: space-around;
} }
.user_info { .user_info {
@ -95,7 +103,7 @@ header {
} }
#top { #top {
float: left;
display: inline-block; display: inline-block;
justify-content: space-between; justify-content: space-between;
margin-left: auto; margin-left: auto;

View File

@ -81,12 +81,6 @@ function postLogin(target,type, data, key,callBackResult) {
if (httpRequest.readyState === XMLHttpRequest.DONE) { if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) { if (httpRequest.status === 200) {
try { 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) callBackResult(httpRequest.response)
document.location.href = document.location document.location.href = document.location
} catch (e) { } 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() { function logout() {
if(document.cookie.split(";").length > 1) { // retrieve all cookies
document.cookie.split(";").forEach(function (v,i,a){ document.cookie = "access=; expires=Thu, 01 Jan 1970 00:00:01 GMT;"
if(v.search("S33-DATA") > 0) { document.cookie = "refresh=; expires=Thu, 01 Jan 1970 00:00:01 GMT;"
document.cookie.replace(v,"S33-DATA=")
} else {
}
})
} else {
document.cookie = "S33-DATA="
}
let logOutUrl = getMainPath() + "/user/logout.ajax"; let logOutUrl = getMainPath() + "/user/logout.ajax";
post(logOutUrl,"","","", function (resultData) { post(logOutUrl,"","","", function (resultData) {
alert(resultData) alert(resultData)
@ -159,7 +156,10 @@ function onclickLogin(type, keyword) {
'user_pw': user_pw.value, 'user_pw': user_pw.value,
} }
postLogin(getMainPath()+"/user/login.ajax",type,JSON.stringify(data),keyword, function (resultData) { 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]+";"
}) })
} }

View File

@ -28,6 +28,7 @@
console.log(window.c) console.log(window.c)
console.log(style.getPropertyValue('--FooterHeight')) console.log(style.getPropertyValue('--FooterHeight'))
console.log(style.getPropertyValue('--TopHeight')) console.log(style.getPropertyValue('--TopHeight'))
document.querySelector("#title_field").value = baseData.title
var editorHeght = ( var editorHeght = (
document.querySelector('#main_layer').getBoundingClientRect().height - document.querySelector('#main_layer').getBoundingClientRect().height -
( (
@ -88,9 +89,9 @@
<h1>권한이 없는 뎁쇼?!</h1> <h1>권한이 없는 뎁쇼?!</h1>
</th:block> </th:block>
<th:block th:if="${PERMISSION == 'OK'}"> <th:block th:if="${PERMISSION == 'OK'}">
<div class="layer">
<input id="title_field" /> <label for="title_field"></label><input id="title_field" class="write_option">
</div>
<div id="editor" ></div> <div id="editor" ></div>
<div class="write_controllbox"> <div class="write_controllbox">

View File

@ -2,10 +2,10 @@
<html xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html"> <html xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html">
<th:block th:fragment="header"> <th:block th:fragment="header">
<header> <header>
<div id="top"> <div id="top">
<td><h3><a aria-label="goToMain" style="color: white" href="javascript:mainPath()" title="goToMain">HOME</a></h3></td> <td><h3><a aria-label="goToMain" style="color: white" href="javascript:mainPath()" title="goToMain">HOME</a></h3></td>
</div> </div>
<th:block th:if="${PERMISSION != 'OK'}"> <th:block th:if="${PERMISSION != 'OK'}">
<script th:inline="javascript"> <script th:inline="javascript">
document.addEventListener("DOMContentLoaded", addListen); document.addEventListener("DOMContentLoaded", addListen);
@ -18,6 +18,13 @@
</div> </div>
</th:block> </th:block>
<th:block th:if="${PERMISSION == 'OK'}"> <th:block th:if="${PERMISSION == 'OK'}">
<div></div>
<td><h3><a aria-label="create New Post" style="color: white" href="javascript:gotoWrite()" title="create New Post">create New Post</a></h3></td>
<td><h3><a aria-label="where's Bum" style="color: white" href="javascript:gotoWhere()" title="where's Bum">where's Bum</a></h3></td>
<td><h3><a aria-label="modify & open Post" style="color: white" href="javascript:gotoModify()" title="modify & open Post">modify & open Post</a></h3></td>
<div></div>
<div></div>
<div></div>
<div class="user_info" > <div class="user_info" >
<td><h3><a aria-label="logout" style="color: white" href="javascript:logout()" title="logout">logout</a></h3></td> <td><h3><a aria-label="logout" style="color: white" href="javascript:logout()" title="logout">logout</a></h3></td>
</div> </div>

View File

@ -4,26 +4,17 @@
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns="http://www.w3.org/1999/html"> xmlns="http://www.w3.org/1999/html">
<head> <head>
<!-- <th:block layout:replace="fragments/includes" ></th:block>-->
<th:block th:replace="~{fragments/includes :: includes}"></th:block> <th:block th:replace="~{fragments/includes :: includes}"></th:block>
<!-- layout:fragment="head" -->
<th:block layout:fragment="head"></th:block> <th:block layout:fragment="head"></th:block>
<!-- <th:block layout:replace="fragments/title" ></th:block> -->
<th:block th:replace="~{fragments/title :: title}"></th:block> <th:block th:replace="~{fragments/title :: title}"></th:block>
</head> </head>
<body> <body>
<!--<th:block th:replace="fragments/header :: header"></th:block>-->
<th:block th:replace="~{fragments/header :: header}"></th:block> <th:block th:replace="~{fragments/header :: header}"></th:block>
<!--<th:block layout:fragment="content"></th:block>-->
<th:block layout:fragment="content"></th:block> <th:block layout:fragment="content"></th:block>
<div class="dim_layer"> <div class="dim_layer">
<div class="dimBg"></div> <div class="dimBg"></div>
<th:block layout:fragment="popup_layer"></th:block> <th:block layout:fragment="popup_layer"></th:block>
</div> </div>
<!--<th:block th:replace="fragments/footer :: footer"></th:block>-->
<th:block th:replace="~{fragments/footer :: footer}"></th:block> <th:block th:replace="~{fragments/footer :: footer}"></th:block>
</body> </body>
</html> </html>