...
This commit is contained in:
parent
576932da3d
commit
cd1072430e
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
@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))
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -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")
|
||||
|
||||
@ -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");
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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<ResponceResult> {
|
||||
fun login(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity<LoginResult> {
|
||||
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<ResponceResult> {
|
||||
|
||||
@ -12,6 +12,7 @@ open class ResponceResult : BaseResult() {
|
||||
@Getter
|
||||
open class LoginResult : ResponceResult() {
|
||||
var token: String? = null
|
||||
var refresh: String? = null
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
||||
@ -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<TokenData, String> {
|
||||
fun findBytokenKey(tokenKey: String): Mono<TokenData>
|
||||
fun deleteBytokenKey(tokenKey: String)
|
||||
}
|
||||
@ -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<User, String> {
|
||||
|
||||
fun save(user: User): Mono<User>
|
||||
}
|
||||
|
||||
enum class UserRole {
|
||||
NOT_REGISTERED,READ,WRITE,ADMIN
|
||||
}
|
||||
interface UserService {
|
||||
fun findById(id: String): Mono<User>?
|
||||
fun findByEmail(id: String): Mono<User>?
|
||||
|
||||
132
src/main/kotlin/kr/lunaticbum/back/lun/service/JwtService.kt
Normal file
132
src/main/kotlin/kr/lunaticbum/back/lun/service/JwtService.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
79
src/main/kotlin/kr/lunaticbum/back/lun/utils/JwtUtil.kt
Normal file
79
src/main/kotlin/kr/lunaticbum/back/lun/utils/JwtUtil.kt
Normal 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
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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]+";"
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -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 @@
|
||||
<h1>권한이 없는 뎁쇼?!</h1>
|
||||
</th:block>
|
||||
<th:block th:if="${PERMISSION == 'OK'}">
|
||||
<div class="layer">
|
||||
<input id="title_field" />
|
||||
</div>
|
||||
|
||||
<label for="title_field"></label><input id="title_field" class="write_option">
|
||||
|
||||
<div id="editor" ></div>
|
||||
<div class="write_controllbox">
|
||||
|
||||
|
||||
@ -2,10 +2,10 @@
|
||||
<html xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html">
|
||||
<th:block th:fragment="header">
|
||||
<header>
|
||||
|
||||
<div id="top">
|
||||
<td><h3><a aria-label="goToMain" style="color: white" href="javascript:mainPath()" title="goToMain">HOME</a></h3></td>
|
||||
</div>
|
||||
|
||||
<th:block th:if="${PERMISSION != 'OK'}">
|
||||
<script th:inline="javascript">
|
||||
document.addEventListener("DOMContentLoaded", addListen);
|
||||
@ -18,6 +18,13 @@
|
||||
</div>
|
||||
</th:block>
|
||||
<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" >
|
||||
<td><h3><a aria-label="logout" style="color: white" href="javascript:logout()" title="logout">logout</a></h3></td>
|
||||
</div>
|
||||
|
||||
@ -4,26 +4,17 @@
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
xmlns="http://www.w3.org/1999/html">
|
||||
<head>
|
||||
<!-- <th:block layout:replace="fragments/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:replace="fragments/title" ></th:block> -->
|
||||
<th:block th:replace="~{fragments/title :: title}"></th:block>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--<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>
|
||||
<div class="dim_layer">
|
||||
<div class="dimBg"></div>
|
||||
<th:block layout:fragment="popup_layer"></th:block>
|
||||
</div>
|
||||
<!--<th:block th:replace="fragments/footer :: footer"></th:block>-->
|
||||
<th:block th:replace="~{fragments/footer :: footer}"></th:block>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user