ㅇㅇ 로그인아웃 방식 변경

This commit is contained in:
lunaticbum 2025-08-04 16:35:49 +09:00
parent 3dcb077e2f
commit e39a7d1fd3
34 changed files with 574 additions and 458 deletions

View File

@ -22,6 +22,6 @@ COPY ${JAR_FILE} app.jar
EXPOSE 443 EXPOSE 443
#EXPOSE 27012 #EXPOSE 27012
#EXPOSE 3307 #EXPOSE 3307
ENTRYPOINT ["java","-Dtelegram.bot.key=${BOT_KEY}","-Dtelegram.my.id=${TG_MINE}","-Dtelegram.target.id=${TG_TARGET_ID}","-Dweather.api.key=${WEATHER_KEY}","-Dspring.datasource.url=${DATASOURCE_URL}" ,"-Dspring.data.mongodb.uri=${MONGODB_HOST}","-Dspring.data.mongodb.database=${MONGODB_NAME}","-Dspring.datasource.username=${MRA_ADMIN}","-Dspring.datasource.password=${MRA_PW}","-Dresource.handler=${RESOURCE_HANDLER}","-Dresource.location=${RESOURCE_LOCATION}","-Dimage.upload.path=${IMAGE_UPLOAD_PATH}","-Dapi.gg.place=${GAPI_KEY}","-jar","app.jar"]
#ENTRYPOINT ["java","-jar","app.jar","-Dspring-boot.run.arguments=--telegram.bot.key=${BOT_KEY}, --telegram.my.id=${TG_MINE}, --telegram.target.id=${TG_TARGET_ID}, --weather.api.key=${WEATHER_KEY}"] #ENTRYPOINT ["java","-jar","app.jar","-Dspring-boot.run.arguments=--telegram.bot.key=${BOT_KEY}, --telegram.my.id=${TG_MINE}, --telegram.target.id=${TG_TARGET_ID}, --weather.api.key=${WEATHER_KEY}"]
ENTRYPOINT ["java","-Dtelegram.bot.key=${BOT_KEY}","-Dtelegram.my.id=${TG_MINE}","-Dtelegram.target.id=${TG_TARGET_ID}","-Dweather.api.key=${WEATHER_KEY}","-Dspring.datasource.url=${DATASOURCE_URL}" ,"-Dspring.data.mongodb.uri=${MONGODB_HOST}","-Dspring.data.mongodb.database=${MONGODB_NAME}","-Dspring.datasource.username=${MRA_ADMIN}","-Dspring.datasource.password=${MRA_PW}","-Dresource.handler=${RESOURCE_HANDLER}","-Dresource.location=${RESOURCE_LOCATION}","-Dimage.upload.path=${IMAGE_UPLOAD_PATH}","-Dapi.gg.place=${GAPI_KEY}","-jar","app.jar"]
#-Dtelegram.bot.key=bot7934509464:AAE_xUbICxMdywLGnxo7BkeIqA1nVza4P9w -Dtelegram.target.id=71476436 -Dtelegram.my.id=71476436 -Dweather.api.key=de574a260b1f474d99955729241909 -Dspring.datasource.url=jdbc:mariadb://mra.sbspace.synology.me -Dspring.data.mongodb.uri=mongodb://lun_admin:VioPup*383@mongo.sbspace.synology.me/?wtimeoutMS=300&connectTimeoutMS=500&socketTimeoutMS=200 -Dspring.data.mongodb.database=lun_db -Dspring.datasource.username=lun_admin -Dspring.datasource.password=VioPup*383 -Dresource.handler=/blog/post/image/** -Dresource.location=file:///imgUpload -Dimage.upload.path=imgUpload #-Dtelegram.bot.key=bot7934509464:AAE_xUbICxMdywLGnxo7BkeIqA1nVza4P9w -Dtelegram.target.id=71476436 -Dtelegram.my.id=71476436 -Dweather.api.key=de574a260b1f474d99955729241909 -Dspring.datasource.url=jdbc:mariadb://mra.sbspace.synology.me -Dspring.data.mongodb.uri=mongodb://lun_admin:VioPup*383@mongo.sbspace.synology.me/?wtimeoutMS=300&connectTimeoutMS=500&socketTimeoutMS=200 -Dspring.data.mongodb.database=lun_db -Dspring.datasource.username=lun_admin -Dspring.datasource.password=VioPup*383 -Dresource.handler=/blog/post/image/** -Dresource.location=file:///imgUpload -Dimage.upload.path=imgUpload

View File

@ -50,6 +50,8 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
implementation("org.springframework.boot:spring-boot-starter-thymeleaf") implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity6")
implementation("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect") implementation("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect")
implementation ("org.jsoup:jsoup:1.18.1") implementation ("org.jsoup:jsoup:1.18.1")
@ -104,6 +106,7 @@ tasks.withType<Test> {
tasks.jar { tasks.jar {
archiveFileName.set("app.jar")
manifest { manifest {
attributes["Main-Class"] = "kr.lunaticbum.back.lun.LunApplicationKt" attributes["Main-Class"] = "kr.lunaticbum.back.lun.LunApplicationKt"
} }

View File

@ -4,6 +4,8 @@ import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Configuration
import org.springframework.http.CacheControl import org.springframework.http.CacheControl
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
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
import java.time.Duration import java.time.Duration
@ -28,10 +30,13 @@ class AppConfig : WebMvcConfigurer {
registry.addResourceHandler(resourceHandler).addResourceLocations(resourceLocation).setCacheControl(cacheControl) registry.addResourceHandler(resourceHandler).addResourceLocations(resourceLocation).setCacheControl(cacheControl)
} }
override fun addInterceptors(registry: InterceptorRegistry) { @Bean
registry.addInterceptor(authInterceptor()) fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder()
super.addInterceptors(registry) // override fun addInterceptors(registry: InterceptorRegistry) {
} // registry.addInterceptor(authInterceptor())
// .addPathPatterns("**/*.bs", "**/*.bjx")
// super.addInterceptors(registry)
// }
// @Bean // @Bean

View File

@ -8,9 +8,9 @@ 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.model.UserManager import kr.lunaticbum.back.lun.model.UserManager
import kr.lunaticbum.back.lun.service.JwtService
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.lang.Nullable import org.springframework.lang.Nullable
import org.springframework.security.web.authentication.RememberMeServices
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import org.springframework.web.servlet.HandlerInterceptor import org.springframework.web.servlet.HandlerInterceptor
@ -19,12 +19,8 @@ import org.springframework.web.servlet.ModelAndView
@Component @Component
class BumsInterceptor : HandlerInterceptor { class BumsInterceptor : HandlerInterceptor {
@Autowired
lateinit var jwtService : JwtService
@Autowired @Autowired
lateinit var globalEvv : GlobalEnvironment lateinit var globalEvv : GlobalEnvironment
@Autowired
lateinit var userManager: UserManager
val WRITE_PERMISSION_KEY = "PERMISSION" val WRITE_PERMISSION_KEY = "PERMISSION"
@ -36,9 +32,13 @@ class BumsInterceptor : HandlerInterceptor {
// println("==================== BEGIN ====================") // println("==================== BEGIN ====================")
// println("Request URL ===> " + request.requestURL) // println("Request URL ===> " + request.requestURL)
// } // }
return super.preHandle(request, response, handler) return super.preHandle(request, response, handler)
} }
// @Autowired
// lateinit var rememberMeServices: RememberMeServices
@Throws(Exception::class) @Throws(Exception::class)
override fun postHandle( override fun postHandle(
@ -47,68 +47,9 @@ class BumsInterceptor : HandlerInterceptor {
handler: Any, handler: Any,
@Nullable modelAndView: ModelAndView? @Nullable modelAndView: ModelAndView?
) { ) {
var skippResourcesExtension = arrayListOf(".ajax",".js",".css","/tlg/",".api","error").filter { request.requestURI.contains(it)}.size > 0 // if(remeberMe && authResult != null) {
if (!skippResourcesExtension) { // rememberMeServices.loginSuccess(httpServletRequest, responce, authResult)
if (request.requestURI.contains("logout") == false && !request.cookies.isNullOrEmpty() && request.cookies.filter { // }
it.name.equals(
"access"
) && it.value.length > 0
}.size > 0) {
var refreshOk = false;
var accessOk = false;
var access: Cookie? = null
var refresh: Cookie? = null
request.cookies.forEach {
if (it.name.equals("access", true) && jwtService.validateAccessToken(it.value)) {
access = it
accessOk = true
println("==================== accessOk ${accessOk} ======================")
}
}
request.cookies.forEach {
if (it.name.equals("refresh", true) && jwtService.validateRefreshToken(access?.value, it.value)) {
refresh = it
refreshOk = true
println("==================== refreshOk ${refreshOk} ======================")
}
}
if (refreshOk || accessOk) {
request.getSession(true)?.let { session ->
session.setAttribute(WRITE_PERMISSION_KEY, true)
session.maxInactiveInterval = 60 * 5
}
} else {
}
} else if (request.requestURI.contains("logout")) {
request.getSession(true)?.let { session ->
session.invalidate()
session.setAttribute(WRITE_PERMISSION_KEY, false)
}
}
request.cookies?.forEach {
if (it.name.equals("CLEAR", true)) {
request.getSession(false)?.let { session ->
// session.invalidate()
session.setAttribute(WRITE_PERMISSION_KEY, false)
}
}
}
modelAndView?.modelMap?.put(WRITE_PERMISSION_KEY,"NO")
request.getSession(true)?.let {
(it.getAttribute(WRITE_PERMISSION_KEY) as? Boolean)?.let { permission ->
if (permission) {
modelAndView?.modelMap?.put(WRITE_PERMISSION_KEY,"OK")
modelAndView?.modelMap?.put(EncTypeKey, EncType11)
modelAndView?.modelMap?.put(ApiKeyWordKey,"Def")
}
}
}
println("==================== END ======================")
println("===============================================")
}
super.postHandle(request, response, handler, modelAndView) super.postHandle(request, response, handler, modelAndView)
} }

View File

@ -18,19 +18,19 @@ class GlobalEnvironment : EnvironmentAware {
fun padding(key : String) = pad.plus(key).plus(pad) fun padding(key : String) = pad.plus(key).plus(pad)
} }
@Value("\${telegram.bot.key}") @Value("\${telegram.bot.key}")
var telegramBotKey: String? = "" lateinit var telegramBotKey: String
@Value("\${telegram.my.id}") @Value("\${telegram.my.id}")
var telegramMyId: String? = "" lateinit var telegramMyId: String
@Value("\${telegram.target.id}") @Value("\${telegram.target.id}")
var telegramTargetId: String? = "" lateinit var telegramTargetId: String
@Value("\${weather.api.key}") @Value("\${weather.api.key}")
var weatherApiKey: String? = "" lateinit var weatherApiKey: String
@Value("\${api.gg.place}") @Value("\${api.gg.place}")
var gapiKey : String? = "" lateinit var gapiKey: String
// @Value("jwt.access-secret") // @Value("jwt.access-secret")
var ACCESS_SECRET_KEY: String = "l00u00n00a00t00i00c00b00u00m00a00c00sk" var ACCESS_SECRET_KEY: String = "l00u00n00a00t00i00c00b00u00m00a00c00sk"
@ -42,6 +42,7 @@ class GlobalEnvironment : EnvironmentAware {
var REFRESH_EXPIRATION: Long = 60 * 30 * 1000L var REFRESH_EXPIRATION: Long = 60 * 30 * 1000L
override fun setEnvironment(environment: Environment) { override fun setEnvironment(environment: Environment) {
environment.activeProfiles.forEach { println(it) }
println ("telegramBotKey $telegramBotKey") println ("telegramBotKey $telegramBotKey")
println("telegramMyId $telegramMyId") println("telegramMyId $telegramMyId")
println("telegramMyId $telegramTargetId") println("telegramMyId $telegramTargetId")

View File

@ -3,6 +3,7 @@ package kr.lunaticbum.back.lun.configs
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
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.model.UserManager
import kr.lunaticbum.back.lun.utils.LogService import kr.lunaticbum.back.lun.utils.LogService
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Bean
@ -11,6 +12,8 @@ import org.springframework.http.HttpMethod
import org.springframework.http.HttpStatus import org.springframework.http.HttpStatus
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.security.access.AccessDeniedException import org.springframework.security.access.AccessDeniedException
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.http.SessionCreationPolicy import org.springframework.security.config.http.SessionCreationPolicy
@ -24,60 +27,87 @@ import org.springframework.web.ErrorResponse
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
class SecurityConfig { class SecurityConfig(
private val userManager: UserManager,
private val bCryptPasswordEncoder: BCryptPasswordEncoder
) {
@Autowired @Autowired
lateinit var logService: LogService lateinit var logService: LogService
@Bean @Bean
fun filterChain(http: HttpSecurity): SecurityFilterChain { fun filterChain(http: HttpSecurity): SecurityFilterChain {
http.csrf { http.csrf { csrf ->
it.ignoringRequestMatchers("/user/joinUser.ajax").disable() csrf.ignoringRequestMatchers("/user/login.bjx", "/user/joinUser.bjx") // 여기 예외 추가
} }.authorizeHttpRequests { auth ->
http.cors { it.disable() } auth
http.headers { .requestMatchers(
it.frameOptions { frameOptionsConfig -> "/", "/home",
frameOptionsConfig.disable() "/bums/where.bs" ,
"/user/login.bs", "/user/signup.bs","/user/login.bjx",
"/css/**", "/js/**", "/images/**", "/webjars/**", "/assets/**").permitAll()
.anyRequest().authenticated()
}.formLogin { form ->
form.loginPage("/user/login.bs")
.defaultSuccessUrl("/", true)
.permitAll()
}.rememberMe { rememberMe ->
rememberMe
.key("BsTs*!12@") // 보통 안전한 키 지정
.tokenValiditySeconds(60 * 60 * 24 * 7) // 7일간 유효
.userDetailsService(userManager) // 사용자 정보 서비스 지정
}.logout { logout ->
logout.logoutUrl("/user/logout.bs").logoutSuccessUrl("/").permitAll()
} }
}
http.authorizeHttpRequests {
logService.log(it.toString())
it.requestMatchers(HttpMethod.POST,"/user/**").permitAll()
// it.requestMatchers(HttpMethod.POST,"/user/**").permitAll()
// it.requestMatchers(HttpMethod.POST,"/user/**").permitAll()
// it.requestMatchers("/", "/user/**").permitAll()
// .requestMatchers(".ajax").permitAll()
// it.requestMatchers("/", "/user/joinUser.api").permitAll()
// it.requestMatchers("user/joinUser.api").permitAll()
it.requestMatchers("/blog/viewer/**").permitAll()
it.anyRequest().permitAll()
// .requestMatchers("/", "/login/**").permitAll()
// .requestMatchers("/admins/**", "/api/v1/admins/**").hasRole(Role.ADMIN.name)
// .anyRequest().authenticated()
}
http.sessionManagement {
it.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
.exceptionHandling { it ->
it.authenticationEntryPoint(unauthorizedEntryPoint)
.accessDeniedHandler(accessDeniedHandler)
}
// .formLogin { formLogin ->
// formLogin
// .loginPage("/user/join")
////// .usernameParameter("username")
////// .passwordParameter("password")
// .loginProcessingUrl("/user/joinUser.api")
// .defaultSuccessUrl("/", true)
// }
return http.build() return http.build()
} }
@Bean
fun authenticationManager(http: HttpSecurity): AuthenticationManager {
val authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder::class.java)
authenticationManagerBuilder
.userDetailsService(userManager)
.passwordEncoder(bCryptPasswordEncoder)
return authenticationManagerBuilder.build() // .and() 없이 직접 build() 호출
}
// @Bean
// fun filterChain(http: HttpSecurity): SecurityFilterChain {
//
// http.csrf {
// it.ignoringRequestMatchers("/user/joinUser.bjx").disable()
// }
// http.cors { it.disable() }
// http.headers {
// it.frameOptions { frameOptionsConfig ->
// frameOptionsConfig.disable()
// }
// }
//
// http.authorizeHttpRequests {
// logService.log(it.toString())
// it.requestMatchers(HttpMethod.POST,"/user/**").permitAll()
// it.requestMatchers("/blog/viewer/**").permitAll()
// it.anyRequest().permitAll()
// }
// http.sessionManagement {
// it.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// }
// .exceptionHandling { it ->
// it.authenticationEntryPoint(unauthorizedEntryPoint)
// .accessDeniedHandler(accessDeniedHandler)
// }
//// .formLogin { formLogin ->
//// formLogin
//// .loginPage("/user/join")
//////// .usernameParameter("username")
//////// .passwordParameter("password")
//// .loginProcessingUrl("/user/joinUser.api")
//// .defaultSuccessUrl("/", true)
//// }
//
// return http.build()
// }
private val unauthorizedEntryPoint = private val unauthorizedEntryPoint =
AuthenticationEntryPoint { request: HttpServletRequest?, response: HttpServletResponse, authException: AuthenticationException? -> AuthenticationEntryPoint { request: HttpServletRequest?, response: HttpServletResponse, authException: AuthenticationException? ->
val fail: ErrorResponse = ErrorResponse.create( Throwable("아직 못들어와"), val fail: ErrorResponse = ErrorResponse.create( Throwable("아직 못들어와"),

View File

@ -13,7 +13,6 @@ 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.model.* import kr.lunaticbum.back.lun.model.*
import kr.lunaticbum.back.lun.service.JwtService
import kr.lunaticbum.back.lun.utils.LogService import kr.lunaticbum.back.lun.utils.LogService
import kr.lunaticbum.back.lun.utils.getFileExtension import kr.lunaticbum.back.lun.utils.getFileExtension
import net.coobird.thumbnailator.Thumbnails import net.coobird.thumbnailator.Thumbnails
@ -28,6 +27,8 @@ import org.springframework.core.io.Resource
import org.springframework.core.io.UrlResource import org.springframework.core.io.UrlResource
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile import org.springframework.web.multipart.MultipartFile
import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.WebClient
@ -55,7 +56,7 @@ class BlogController() {
@Autowired @Autowired
lateinit var logService: LogService lateinit var logService: LogService
val WRITE_PERMISSION_KEY = "PERMISSION" val WRITE_PERMISSION_KEY = "PERMISSION"
@GetMapping("write/{token}","write") @GetMapping("write/{token}","write.bs")
fun writ(@PathVariable token : String? ) : ResultMV{ fun writ(@PathVariable token : String? ) : ResultMV{
val vm = ResultMV("content/blog/write") val vm = ResultMV("content/blog/write")
if (token.equals(TEMPTOKEN)) { if (token.equals(TEMPTOKEN)) {
@ -67,17 +68,10 @@ class BlogController() {
} else { } else {
vm.modelMap.put(WRITE_PERMISSION_KEY,"NO") vm.modelMap.put(WRITE_PERMISSION_KEY,"NO")
} }
// 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")
// }
return vm return vm
} }
@PostMapping("post.ajax") @PostMapping("post.bjx")
fun post(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity<ResponceResult> { fun post(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity<ResponceResult> {
logService.log(httpServletRequest.requestURI) logService.log(httpServletRequest.requestURI)
logService.log(jsonString) logService.log(jsonString)
@ -164,28 +158,27 @@ class BlogController() {
return vm return vm
} }
@Autowired
lateinit var jwtService : JwtService
@GetMapping("modify") @GetMapping("modify.bs")
fun modify(httpServletRequest: HttpServletRequest, @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")
vm.modelMap.put(WRITE_PERMISSION_KEY,"NO") val authentication = SecurityContextHolder.getContext().authentication
httpServletRequest.getSession(true)?.let { session -> val principal = authentication.principal
(session.getAttribute(WRITE_PERMISSION_KEY) as? Boolean)?.let { if (principal is UserDetails) {
postManageg.find20()?.apply { val username = principal.username
forEach { // 추가 정보 사용 가능
it.title = URLDecoder.decode(it.title) postManageg.find20()?.apply {
val content = URLDecoder.decode(it.content) forEach {
it.content = if (content.length > 50) content.substring(0,150) else content it.title = URLDecoder.decode(it.title)
} val content = URLDecoder.decode(it.content)
vm.modelMap.put("chunkedPosts", this.chunked(3)) it.content = if (content.length > 50) content.substring(0,150) else content
} }
vm.modelMap.put(WRITE_PERMISSION_KEY,"OK") vm.modelMap.put("chunkedPosts", this.chunked(3))
vm.modelMap.put("path","editor/")
vm.modelMap.put("SK",token)
} }
vm.modelMap.put(WRITE_PERMISSION_KEY,"OK")
vm.modelMap.put("path","editor/")
vm.modelMap.put("SK",token)
} }
vm.modelMap.put("rowKey","chunkedPosts_") vm.modelMap.put("rowKey","chunkedPosts_")
return vm return vm

View File

@ -30,7 +30,7 @@ class BumsPrivate {
@Autowired @Autowired
lateinit var locationService: LocationLogService lateinit var locationService: LocationLogService
@GetMapping("where") @GetMapping("where.bs")
fun where() : ResultMV { fun where() : ResultMV {
val m = ResultMV("content/private/where") val m = ResultMV("content/private/where")

View File

@ -9,7 +9,6 @@ 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.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.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
@ -17,12 +16,18 @@ 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.ResponseCookie
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.security.authentication.AuthenticationManager
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.*
import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.WebClient
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.web.authentication.RememberMeServices
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
import java.io.File import java.io.File
import java.util.* import java.util.*
import javax.naming.AuthenticationException
@RestController @RestController
@ -38,10 +43,8 @@ class UserController {
@Autowired @Autowired
lateinit var userManager: UserManager lateinit var userManager: UserManager
@Autowired
lateinit var jwtService : JwtService
@GetMapping("join") @GetMapping("join.bs")
fun hello(httpServletRequest: HttpServletRequest): ResultMV { fun hello(httpServletRequest: HttpServletRequest): ResultMV {
logService.log("onJoin") logService.log("onJoin")
val vm = ResultMV("content/user/join") val vm = ResultMV("content/user/join")
@ -60,7 +63,7 @@ class UserController {
return vm return vm
} }
@GetMapping("login") @GetMapping("login.bs")
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")
@ -69,72 +72,93 @@ class UserController {
return vm return vm
} }
// @GetMapping("logout.bs")
// fun logoutBs(session: HttpSession): ResultMV {
// session.invalidate(); // 세션 날리기 (로그아웃)
// return "redirect:/"; // 홈으로 이동
// }
@Autowired
lateinit var authenticationManager: AuthenticationManager
@ResponseBody @ResponseBody
@PostMapping("login.ajax") @PostMapping("login.bjx")
fun login(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity<LoginResult> { fun login(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity<LoginResult> {
try { try {
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 user : User? = null
var tokenData : TokenData? = null var tokenData : TokenData? = null
jsonString.extractModelData { exception, originDataString -> var remeberMe = false
if (exception == null) { var authResult : Authentication? = null
logService.log(originDataString) jsonString.extractModelData { exception, originDataString ->
val target = Gson().fromJson(originDataString, User::class.java) ?: User() if (exception == null) {
user = userManager.findById(target.user_id?.trim() ?: "")?.block() logService.log("originDataString >>> $originDataString")
if (user == null && ((target.user_id?.trim()?.length ?: 0) > 3 == true)) { val target = Gson().fromJson(originDataString, User::class.java) ?: User()
user = userManager.findByEmail(target.user_id?.trim() ?: "")?.block() try {
} val authToken = UsernamePasswordAuthenticationToken(target.user_id, target.user_pw)
if (user != null) { authResult = authenticationManager.authenticate(authToken) // 인증 시도
if(userManager.isCorrectUser(user!!,target.user_pw!!)){ println("authResult >>>> $authResult")
tokenData = jwtService.generate(user!!) // 인증 성공 시 SecurityContextHolder에 인증 정보 저장
} else { SecurityContextHolder.getContext().authentication = authResult
lResultMsg = "is wrong infomation id or passord"
lResultCode = 7100
}
} else {
lResultMsg = "not founding user[can't find same id,email.. ]"
lResultCode = 7100
}
} else {
exception.printStackTrace()
lResultMsg = exception.message ?: "unknown exception"
lResultCode = 7999
}
}
val responce = ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).headers {
}.body(LoginResult().apply { // 인증 정보가 담긴 SecurityContext를 세션에 저장
this.isOk = lResultCode == 0 val session = httpServletRequest.getSession(true)
this.resultCode = lResultCode session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext())
this.resultMsg = lResultMsg println("authResult >>>> LOGIN OK")
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=","") val principal = authResult?.principal
}).apply { if (principal is UserDetails) {
val username = principal.username
val session = httpServletRequest.getSession(true)
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext())
remeberMe = target.remeberMe ?: false
} else {
lResultMsg = "is wrong infomation id or passord"
lResultCode = 7100
}
} catch (e: Exception) {
e.printStackTrace()
}
} else {
exception.printStackTrace()
lResultMsg = exception.message ?: "unknown exception"
lResultCode = 7999
}
}
val responce = ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).headers {
}.body(LoginResult().apply {
this.isOk = lResultCode == 0
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
}catch (e: Exception){
return ResponseEntity.internalServerError().contentType(MediaType.APPLICATION_JSON).headers {
}.body(LoginResult().apply {
this.isOk = false
this.resultCode = -999
this.resultMsg = e.message ?: "unknown exception"
this.token = ""
this.refresh = ""
}).apply {
} }
}
return responce
}catch (e: Exception){
return ResponseEntity.internalServerError().contentType(MediaType.APPLICATION_JSON).headers {
}.body(LoginResult().apply {
this.isOk = false
this.resultCode = -999
this.resultMsg = e.message ?: "unknown exception"
this.token = ""
this.refresh = ""
}).apply {
}
}
} }
@ -149,7 +173,7 @@ class UserController {
} }
@ResponseBody @ResponseBody
@PostMapping("logout.ajax") @PostMapping("logout.bjx")
fun logout(httpServletRequest: HttpServletRequest) : ResponseEntity<ResponceResult> { fun logout(httpServletRequest: HttpServletRequest) : ResponseEntity<ResponceResult> {
val responce = ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(ResponceResult().apply { val responce = ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(ResponceResult().apply {
@ -158,7 +182,7 @@ class UserController {
} }
@ResponseBody @ResponseBody
@PostMapping("joinUser.ajax") @PostMapping("joinUser.bjx")
fun joinUser(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity<ResponceResult> { fun joinUser(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity<ResponceResult> {
logService.log("${httpServletRequest.requestURI}") logService.log("${httpServletRequest.requestURI}")
logService.log(jsonString) logService.log(jsonString)
@ -174,9 +198,6 @@ class UserController {
}else if (userManager.findById(user!!.user_id!!)?.block() != null) { }else if (userManager.findById(user!!.user_id!!)?.block() != null) {
lResultCode = 7001 lResultCode = 7001
lResultMsg = "user insert Fail Reason : already has Same Id" lResultMsg = "user insert Fail Reason : already has Same Id"
}else if (userManager.findByEmail(user!!.user_email!!)?.block() != null ) {
lResultCode = 7002
lResultMsg = "user insert Fail Reason : already has Same Email"
} else { } else {
u = userManager.save(user).block() u = userManager.save(user).block()
} }

View File

@ -39,10 +39,12 @@ class User {
@CreatedDate @CreatedDate
var user_join: Long = 0L var user_join: Long = 0L
var user_name: String? = null // var user_name: String? = null
var isAccept : String? = null var isAccept : String? = null
var isAdmin : String? = null var isAdmin : String? = null
var remeberMe : Boolean? = false
fun checkValid() : Boolean { fun checkValid() : Boolean {
if ( if (
((user_id?.length ?: 0) > 5) && ((user_id?.length ?: 0) > 5) &&
@ -111,8 +113,8 @@ interface UserRepository : ReactiveMongoRepository<User, String> {
@Query("{user_id :?0}") @Query("{user_id :?0}")
override fun findById(user_id: String): Mono<User> override fun findById(user_id: String): Mono<User>
@Query("{user_email :?0}") // @Query("{user_email :?0}")
fun findByEmail(user_email: String): Mono<User> // fun findByEmail(user_email: String): Mono<User>
fun save(user: User): Mono<User> fun save(user: User): Mono<User>
@ -123,23 +125,24 @@ enum class UserRole {
} }
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>?
} }
@Service @Service
class UserManager : UserService , UserDetailsService { class UserManager(
private val passwordEncoder: PasswordEncoder
) : UserService , UserDetailsService {
@Autowired @Autowired
private lateinit var logService: LogService private lateinit var logService: LogService
@Autowired @Autowired
private lateinit var userRepository: UserRepository private lateinit var userRepository: UserRepository
@Autowired
private lateinit var bCryptPasswordEncoder: PasswordEncoder
override fun findByEmail(id: String): Mono<User>? {
return userRepository.findByEmail(id) // override fun findByEmail(id: String): Mono<User>? {
} // return userRepository.findByEmail(id)
// }
override fun findById(id: String): Mono<User>? { override fun findById(id: String): Mono<User>? {
return userRepository.findById(id) return userRepository.findById(id)
@ -149,19 +152,22 @@ class UserManager : UserService , UserDetailsService {
fun save(user: User): Mono<User> { fun save(user: User): Mono<User> {
println("saved user before ${user}") println("saved user before ${user}")
user.hashPassword(bCryptPasswordEncoder) user.hashPassword(passwordEncoder)
return userRepository.save(user) return userRepository.save(user)
} }
fun isCorrectUser(user: User, password : String) : Boolean { fun isCorrectUser(user: User, password : String) : Boolean {
return user.checkPassword(password,bCryptPasswordEncoder) return user.checkPassword(password,passwordEncoder)
} }
override fun loadUserByUsername(username: String?): UserDetails { override fun loadUserByUsername(username: String?): UserDetails {
var user = findById(username!!)?.blockOptional(Duration.ofMillis(5000L))?.get() ?: User()
logService.log("username ${username}") logService.log("username ${username}")
user.hashPassword(bCryptPasswordEncoder) var user = findById(username!!)?.blockOptional(Duration.ofMillis(5000L))?.get() ?: User()
return org.springframework.security.core.userdetails.User.builder().username(user.user_id ?: "").password(user.user_pw).roles(if ("Y".equals(user.isAdmin)) Role.ADMIN.name else {Role.USER.name}).build() // user.hashPassword(passwordEncoder)
return org.springframework.security.core.userdetails.User.builder()
.username(user.user_id ?: "")
.password(user.user_pw)
.roles(if ("Y".equals(user.isAdmin)) Role.ADMIN.name else {Role.USER.name}).build()
} }
} }

View File

@ -1,185 +1,185 @@
package kr.lunaticbum.back.lun.service //package kr.lunaticbum.back.lun.service
//
import io.jsonwebtoken.Claims //import io.jsonwebtoken.Claims
import io.jsonwebtoken.Jws //import io.jsonwebtoken.Jws
import io.jsonwebtoken.Jwts //import io.jsonwebtoken.Jwts
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.configs.GlobalEnvironment //import kr.lunaticbum.back.lun.configs.GlobalEnvironment
import kr.lunaticbum.back.lun.configs.JwtGenerator //import kr.lunaticbum.back.lun.configs.JwtGenerator
import kr.lunaticbum.back.lun.configs.JwtRule //import kr.lunaticbum.back.lun.configs.JwtRule
import kr.lunaticbum.back.lun.configs.TokenStatus //import kr.lunaticbum.back.lun.configs.TokenStatus
import kr.lunaticbum.back.lun.model.* //import kr.lunaticbum.back.lun.model.*
import kr.lunaticbum.back.lun.utils.BusinessException //import kr.lunaticbum.back.lun.utils.BusinessException
import kr.lunaticbum.back.lun.utils.ErrorCode //import kr.lunaticbum.back.lun.utils.ErrorCode
import kr.lunaticbum.back.lun.utils.JwtUtil //import kr.lunaticbum.back.lun.utils.JwtUtil
import lombok.extern.slf4j.Slf4j //import lombok.extern.slf4j.Slf4j
import org.springframework.beans.factory.annotation.Autowired //import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpHeaders //import org.springframework.http.HttpHeaders
import org.springframework.http.ResponseCookie //import org.springframework.http.ResponseCookie
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken //import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication //import org.springframework.security.core.Authentication
import org.springframework.security.core.token.Token //import org.springframework.security.core.token.Token
import org.springframework.security.core.userdetails.UserDetails //import org.springframework.security.core.userdetails.UserDetails
import org.springframework.stereotype.Service //import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional //import org.springframework.transaction.annotation.Transactional
import java.security.Key //import java.security.Key
import java.time.Duration //import java.time.Duration
import java.util.* //import java.util.*
import java.util.function.Consumer //import java.util.function.Consumer
//
//
@Service //@Service
@Transactional(readOnly = true) //@Transactional(readOnly = true)
@Slf4j //@Slf4j
class JwtService { //class JwtService {
//
@Autowired // @Autowired
lateinit var globalEvv : GlobalEnvironment // lateinit var globalEvv : GlobalEnvironment
//
@Autowired // @Autowired
private lateinit var jwtGenerator: JwtGenerator // private lateinit var jwtGenerator: JwtGenerator
//
@Autowired // @Autowired
private lateinit var jwtUtil: JwtUtil // private lateinit var jwtUtil: JwtUtil
//
//
@Autowired // @Autowired
private lateinit var customUserDetailsService: UserManager // private lateinit var customUserDetailsService: UserManager
//
@Autowired // @Autowired
private lateinit var tokenRepository : TokenDataRepository // private lateinit var tokenRepository : TokenDataRepository
//
// private val ACCESS_SECRET_KEY: Key = jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY) //// private val ACCESS_SECRET_KEY: Key = jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY)
// private val REFRESH_SECRET_KEY: Key = jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY) //// private val REFRESH_SECRET_KEY: Key = jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY)
//
fun validateUser(requestUser: User) { // fun validateUser(requestUser: User) {
if (requestUser.getRole() === UserRole.NOT_REGISTERED) { // if (requestUser.getRole() === UserRole.NOT_REGISTERED) {
throw BusinessException(ErrorCode.NOT_AUTHENTICATED_USER) // throw BusinessException(ErrorCode.NOT_AUTHENTICATED_USER)
} // }
} // }
//
@Transactional // @Transactional
fun generate(requestUser: User): TokenData? { // fun generate(requestUser: User): TokenData? {
var accessToken = jwtGenerator.generateAccessToken(jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY), globalEvv.ACCESS_EXPIRATION, requestUser) // 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 refreshToken = jwtGenerator.generateRefreshToken(jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY), globalEvv.REFRESH_EXPIRATION, requestUser)
var token = TokenData(accessToken, refreshToken) // var token = TokenData(accessToken, refreshToken)
return tokenRepository.save(token).block() // return tokenRepository.save(token).block()
} // }
//
//
private fun setTokenToCookie(tokenPrefix: String, token: String, maxAgeSeconds: Long): ResponseCookie { // private fun setTokenToCookie(tokenPrefix: String, token: String, maxAgeSeconds: Long): ResponseCookie {
return ResponseCookie.from(tokenPrefix, token) // return ResponseCookie.from(tokenPrefix, token)
.path("/") // .path("/")
.maxAge(maxAgeSeconds) // .maxAge(maxAgeSeconds)
.httpOnly(true) // .httpOnly(true)
.sameSite("None") // .sameSite("None")
.secure(true) // .secure(true)
.build() // .build()
} // }
//
fun validateAccessToken(token: String?): Boolean { // fun validateAccessToken(token: String?): Boolean {
return jwtUtil.getTokenStatus(token, jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY)) == TokenStatus.AUTHENTICATED // return jwtUtil.getTokenStatus(token, jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY)) == TokenStatus.AUTHENTICATED
} // }
//
fun validateRefreshToken(token: String?, refreshToken: String?): Boolean { // fun validateRefreshToken(token: String?, refreshToken: String?): Boolean {
try { // try {
val isRefreshValid = jwtUtil.getTokenStatus(refreshToken, jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY)) == TokenStatus.AUTHENTICATED // val isRefreshValid = jwtUtil.getTokenStatus(refreshToken, jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY)) == TokenStatus.AUTHENTICATED
val storedToken: TokenData? = tokenRepository.findBytokenKey(token ?: "").block(Duration.ofSeconds(10)) // val storedToken: TokenData? = tokenRepository.findBytokenKey(token ?: "").block(Duration.ofSeconds(10))
val isTokenMatched: Boolean = storedToken?.refreshToken.equals(refreshToken) // val isTokenMatched: Boolean = storedToken?.refreshToken.equals(refreshToken)
return isRefreshValid && isTokenMatched // return isRefreshValid && isTokenMatched
} catch (e :Exception){ // } catch (e :Exception){
//
} // }
return false // return false
} // }
//
fun resolveTokenFromCookie(request: HttpServletRequest, tokenPrefix: JwtRule?): String { // fun resolveTokenFromCookie(request: HttpServletRequest, tokenPrefix: JwtRule?): String {
val cookies = request.cookies ?: throw BusinessException(ErrorCode.JWT_TOKEN_NOT_FOUND) // val cookies = request.cookies ?: throw BusinessException(ErrorCode.JWT_TOKEN_NOT_FOUND)
return jwtUtil.resolveTokenFromCookie(cookies, tokenPrefix!!) // return jwtUtil.resolveTokenFromCookie(cookies, tokenPrefix!!)
} // }
//
fun getAuthentication(token: String): Authentication { // fun getAuthentication(token: String): Authentication {
val principal: UserDetails = customUserDetailsService.loadUserByUsername(getUserPk(token, jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY))) // val principal: UserDetails = customUserDetailsService.loadUserByUsername(getUserPk(token, jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY)))
return UsernamePasswordAuthenticationToken(principal, "", principal.authorities) // return UsernamePasswordAuthenticationToken(principal, "", principal.authorities)
} // }
//
private fun getUserPk(token: String, secretKey: Key): String { // private fun getUserPk(token: String, secretKey: Key): String {
return Jwts.parserBuilder() // return Jwts.parserBuilder()
.setSigningKey(secretKey) // .setSigningKey(secretKey)
.build() // .build()
.parseClaimsJws(token) // .parseClaimsJws(token)
.getBody() // .getBody()
.getSubject() // .getSubject()
} // }
//
fun getIdentifierFromRefresh(refreshToken: String?): String { // fun getIdentifierFromRefresh(refreshToken: String?): String {
try { // try {
return Jwts.parserBuilder() // return Jwts.parserBuilder()
.setSigningKey(jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY)) // .setSigningKey(jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY))
.build() // .build()
.parseClaimsJws(refreshToken) // .parseClaimsJws(refreshToken)
.getBody() // .getBody()
.getSubject() // .getSubject()
} catch (e: Exception) { // } catch (e: Exception) {
throw BusinessException(ErrorCode.INVALID_JWT) // throw BusinessException(ErrorCode.INVALID_JWT)
} // }
} // }
//
fun logout(token: String, response: HttpServletResponse) { // fun logout(token: String, response: HttpServletResponse) {
tokenRepository.deleteBytokenKey(token) // tokenRepository.deleteBytokenKey(token)
//
val accessCookie = jwtUtil.resetToken(JwtRule.ACCESS_PREFIX) // val accessCookie = jwtUtil.resetToken(JwtRule.ACCESS_PREFIX)
val refreshCookie = jwtUtil.resetToken(JwtRule.REFRESH_PREFIX) // val refreshCookie = jwtUtil.resetToken(JwtRule.REFRESH_PREFIX)
//
response.addCookie(accessCookie) // response.addCookie(accessCookie)
response.addCookie(refreshCookie) // response.addCookie(refreshCookie)
} // }
fun getUserIdFromToken(token: String?): String? { // fun getUserIdFromToken(token: String?): String? {
try { // try {
return jwtUtil.extractToken(token,jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY))?.body?.get("Identifier") // return jwtUtil.extractToken(token,jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY))?.body?.get("Identifier")
.toString() // .toString()
} catch (e: Exception) { // } catch (e: Exception) {
return null // return null
} // }
} // }
fun getUserIdFromRefresh(token: String?): String? { // fun getUserIdFromRefresh(token: String?): String? {
try { // try {
return jwtUtil.extractToken(token,jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY))?.body?.get("Identifier") // return jwtUtil.extractToken(token,jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY))?.body?.get("Identifier")
.toString() // .toString()
} catch (e: Exception) { // } catch (e: Exception) {
return null // return null
} // }
} // }
//
//
//
fun hasPerrmission(request: HttpServletRequest): Boolean { // fun hasPerrmission(request: HttpServletRequest): Boolean {
var correctUserCheck = -1; // var correctUserCheck = -1;
if (request.requestURI.contains("logout") == false && !request.cookies.isNullOrEmpty() && request.cookies.filter { it.name.equals("access") && 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) {
var access : Cookie?= null // var access : Cookie?= null
var refresh : Cookie?= null // var refresh : Cookie?= null
request.cookies.forEach { // request.cookies.forEach {
if (it.name.equals("access", true) && validateAccessToken(it.value)){ // if (it.name.equals("access", true) && validateAccessToken(it.value)){
access = it // access = it
correctUserCheck += 1 // correctUserCheck += 1
} // }
} // }
request.cookies.forEach { // request.cookies.forEach {
if (it.name.equals("refresh", true) && validateRefreshToken(access?.value,it.value)){ // if (it.name.equals("refresh", true) && validateRefreshToken(access?.value,it.value)){
refresh = it // refresh = it
correctUserCheck += 1 // correctUserCheck += 1
} // }
} // }
if (correctUserCheck > 0) { // if (correctUserCheck > 0) {
println("Response correctUserCheck ===> ${correctUserCheck}") // println("Response correctUserCheck ===> ${correctUserCheck}")
} else { // } else {
println("Response correctUserCheck ===> ${correctUserCheck}") // println("Response correctUserCheck ===> ${correctUserCheck}")
} // }
} else if (request.requestURI.contains("logout")) { // } else if (request.requestURI.contains("logout")) {
//
} // }
return correctUserCheck > 0 // return correctUserCheck > 0
} // }
} //}

View File

@ -65,6 +65,7 @@ fun String.extractModelData(calback : (Exception?,String)->Unit) {
Gson().fromJson<RequestModel>(resultString, RequestModel::class.java).let { model -> Gson().fromJson<RequestModel>(resultString, RequestModel::class.java).let { model ->
model.data?.let { jsonString -> model.data?.let { jsonString ->
try { try {
println("RequestModel ${jsonString}")
calback.invoke(null,model.extractData()) calback.invoke(null,model.extractData())
} catch (e: Exception) { } catch (e: Exception) {
calback.invoke(ExtractDataRequestModelException("Exception on extractData with ${Gson().toJson(model)}", e.cause), jsonString) calback.invoke(ExtractDataRequestModelException("Exception on extractData with ${Gson().toJson(model)}", e.cause), jsonString)

View File

@ -86,6 +86,8 @@ spring.ai.vectorstore.qdrant.collection-name=blama_vectors
spring.ai.ollama.embedding.enabled=true spring.ai.ollama.embedding.enabled=true
resource.handler=.
resource.location=.
server.forward-headers-strategy=framework
#>>>>>>> ab915d0a416c69708f1df1ad76d7a14c779c1f59 #>>>>>>> ab915d0a416c69708f1df1ad76d7a14c779c1f59

View File

@ -168,3 +168,31 @@ footer {
min-height: 5vh; min-height: 5vh;
position: relative; position: relative;
} }
#rememberMe {
width: 20px;
height: 20px;
background-color: lightblue; /* 체크박스 배경색 */
border: 2px solid blue; /* 테두리 색 */
appearance: none; /* 기본 OS 스타일 제거 */
-webkit-appearance: none;
-moz-appearance: none;
cursor: pointer;
position: relative;
}
#rememberMe:checked {
background-color: blue;
}
#rememberMe:checked::after {
content: "";
position: absolute;
top: 3px;
left: 7px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}

View File

@ -31,7 +31,7 @@ function onclickWrite(type, keyword, html) {
baseData.firstPostLat = encodeURIComponent(currentLat) baseData.firstPostLat = encodeURIComponent(currentLat)
baseData.firstPostLon = encodeURIComponent(currentLon) baseData.firstPostLon = encodeURIComponent(currentLon)
} }
let uploadUrl = getMainPath() + "/blog/post.ajax"; let uploadUrl = getMainPath() + "/blog/post.bjx";
if(confirm(JSON.stringify(baseData) + "\n해당 내용으로\n유저 등록 하실??")) { if(confirm(JSON.stringify(baseData) + "\n해당 내용으로\n유저 등록 하실??")) {
post(uploadUrl,type,JSON.stringify(baseData),keyword, function (resultData) { post(uploadUrl,type,JSON.stringify(baseData),keyword, function (resultData) {
alert(resultData) alert(resultData)

View File

@ -147,7 +147,7 @@ function postLogin(target,type, data, key,callBackResult) {
} }
} }
httpRequest.withCredentials = true
httpRequest.open('POST', target, true); httpRequest.open('POST', target, true);
httpRequest.setRequestHeader("Content-Type", "text/plain"); httpRequest.setRequestHeader("Content-Type", "text/plain");
var odd = [] var odd = []
@ -172,15 +172,15 @@ function mainPath() {
} }
function gotoWrite() { function gotoWrite() {
document.location.replace(getMainPath()+"/blog/write") document.location.replace(getMainPath()+"/blog/write.bs")
} }
function gotoModify() { function gotoModify() {
document.location.replace(getMainPath()+"/blog/modify") document.location.replace(getMainPath()+"/blog/modify.bs")
} }
function gotoWhere() { function gotoWhere() {
document.location.replace(getMainPath()+"/bums/where") document.location.replace(getMainPath()+"/bums/where.bs")
} }
function logout() { function logout() {
@ -191,21 +191,35 @@ function logout() {
console.log(document.cookie["JSESSIONID"]) console.log(document.cookie["JSESSIONID"])
document.cookie = "JSESSIONID=; expires=Thu, 01 Jan 1970 00:00:01 GMT;" document.cookie = "JSESSIONID=; expires=Thu, 01 Jan 1970 00:00:01 GMT;"
document.cookie = "CLEAR="+Date.now()+""; document.cookie = "CLEAR="+Date.now()+"";
let logOutUrl = getMainPath() + "/user/logout.ajax"; let logOutUrl = getMainPath() + "/user/logout.bs";
post(logOutUrl,"","","", function (resultData) {
alert("로그아웃 됨요~! 빠염~!")
document.location.replace(document.location)
})
alert("로그아웃 됨요~! 빠염~!")
// 동적으로 form 생성하여 POST 요청 전송
const form = document.createElement('form');
form.method = 'POST';
form.action = getMainPath() + '/user/logout.bs';
// CSRF 토큰을 meta태그 등에서 얻어서 삽입 (예: <meta name="_csrf" content="토큰값">)
const csrfToken = document.querySelector('meta[name="_csrf"]').getAttribute('content');
const csrfParam = document.querySelector('meta[name="_csrf_parameter"]').getAttribute('content');
const csrfInput = document.createElement('input');
csrfInput.type = 'hidden';
csrfInput.name = csrfParam; // 예: "_csrf"
csrfInput.value = csrfToken;
form.appendChild(csrfInput);
document.body.appendChild(form);
form.submit();
} }
function gotoLogin() { function gotoLogin() {
console.log(`location.port >> ${location.port}`) console.log(`location.port >> ${location.port}`)
location.href = getMainPath()+"/login" location.href = getMainPath()+"/login.bs"
} }
function gotoJoin() { function gotoJoin() {
document.location.replace(getMainPath() + "/user/join") document.location.replace(getMainPath() + "/user/join.bs")
} }
function goToView(path,id) { function goToView(path,id) {
@ -218,7 +232,7 @@ function onclickLogin(type, keyword) {
'user_id': user_id.value, 'user_id': user_id.value,
'user_pw': user_pw.value, 'user_pw': user_pw.value,
} }
postLogin(getMainPath()+"/user/login.ajax",type,JSON.stringify(data),keyword, function (data) { postLogin(getMainPath()+"/user/login.bjx",type,JSON.stringify(data),keyword, function (data) {
if (data.isOk) { if (data.isOk) {
document.cookie = "access=" + data.token.split(";")[0]+";" document.cookie = "access=" + data.token.split(";")[0]+";"
@ -229,7 +243,7 @@ function onclickLogin(type, keyword) {
} else { } else {
if (data.resultCode === 7100) { if (data.resultCode === 7100) {
if(confirm(`너 누구임 정보 없는데?!\n${data.resultMsg}[${data.resultCode}]\n가입 할래!?`)){ if(confirm(`너 누구임 정보 없는데?!\n${data.resultMsg}[${data.resultCode}]\n가입 할래!?`)){
document.location.replace(getMainPath() + "/user/join") document.location.replace(getMainPath() + "/user/join.bs")
} }
} else { } else {
alert(`너 누구임?!\n${data.resultMsg}[${data.resultCode}]`) alert(`너 누구임?!\n${data.resultMsg}[${data.resultCode}]`)
@ -320,11 +334,13 @@ function submitLoginForm() {
// const password = document.getElementById('loginPassword').value; // const password = document.getElementById('loginPassword').value;
let user_id = document.getElementById('loginId') let user_id = document.getElementById('loginId')
let user_pw = document.getElementById('loginPassword') let user_pw = document.getElementById('loginPassword')
let rememberMe = document.getElementById('rememberMe')
let data = { let data = {
'user_id': user_id.value, 'user_id': user_id.value,
'user_pw': user_pw.value, 'user_pw': user_pw.value,
'rememberMe' : rememberMe.value,
} }
postLogin(getMainPath()+"/user/login.ajax",user_pw.data,JSON.stringify(data),user_pw.data, function (data) { postLogin(getMainPath()+"/user/login.bjx",user_pw.data,JSON.stringify(data),user_pw.data, function (data) {
closePopup() closePopup()
if (data.isOk) { if (data.isOk) {
document.cookie = "access=" + data.token.split(";")[0]+";" document.cookie = "access=" + data.token.split(";")[0]+";"

View File

@ -62,7 +62,7 @@ function onclickJoin(type, keyword) {
} }
if (user_pw.value === user_pw_check.value) { if (user_pw.value === user_pw_check.value) {
if(confirm(JSON.stringify(data) + "\n해당 내용으로\n유저 등록 하실??")) { if(confirm(JSON.stringify(data) + "\n해당 내용으로\n유저 등록 하실??")) {
post("joinUser.ajax",type,JSON.stringify(data),keyword, function (resultData) { post("joinUser.bjx",type,JSON.stringify(data),keyword, function (resultData) {
alert(resultData) alert(resultData)
}) })
} else { } else {

View File

@ -2,6 +2,8 @@
<html <html
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}" layout:decorate="~{layout/default_layout}"
> >
<th:block layout:fragment="head" id="head"> <th:block layout:fragment="head" id="head">
@ -86,10 +88,10 @@
</section> </section>
<section class="wrapper style2"> <section class="wrapper style2">
<th:block th:if="${PERMISSION != 'OK'}"> <th:block sec:authorize="isAnonymous()">
<h1>권한이 없는 뎁쇼?!</h1> <h1>권한이 없는 뎁쇼?!</h1>
</th:block> </th:block>
<th:block th:if="${PERMISSION == 'OK'}"> <th:block sec:authorize="isAuthenticated()">
<div id="editor" ></div> <div id="editor" ></div>
<div class="write_controllbox"> <div class="write_controllbox">
<div class="write_option btn-example" to="#popLayer1" onclick="openPopup(this)" ></div> <div class="write_option btn-example" to="#popLayer1" onclick="openPopup(this)" ></div>

View File

@ -2,6 +2,7 @@
<html <html
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}"> layout:decorate="~{layout/default_layout}">
<th:block layout:fragment="head"> <th:block layout:fragment="head">
<script type="text/javascript" th:src="@{/js/blog.js}"></script> <script type="text/javascript" th:src="@{/js/blog.js}"></script>
@ -33,10 +34,10 @@
</th:block> </th:block>
<th:block layout:fragment="content" id="content"> <th:block layout:fragment="content" id="content">
<div id="main_layer"> <div id="main_layer">
<th:block th:if="${PERMISSION != 'OK'}"> <th:block sec:authorize="isAnonymous()">
<h1>권한이 없는 뎁쇼?!</h1> <h1>권한이 없는 뎁쇼?!</h1>
</th:block> </th:block>
<th:block th:if="${PERMISSION == 'OK'}"> <th:block sec:authorize="isAuthenticated()">
<div class="post_layer"> <div class="post_layer">
<th:block class="posts_layer" id="posts" th:each="posts ,postsStat: ${chunkedPosts}"> <th:block class="posts_layer" id="posts" th:each="posts ,postsStat: ${chunkedPosts}">
<th:block class="posts_layer" th:class="${#strings.append(rowKey,postsStat.index)}" th:each="post, postStast : ${posts}"> <th:block class="posts_layer" th:class="${#strings.append(rowKey,postsStat.index)}" th:each="post, postStast : ${posts}">

View File

@ -2,6 +2,7 @@
<html <html
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}" layout:decorate="~{layout/default_layout}"
> >
<th:block layout:fragment="head"> <th:block layout:fragment="head">

View File

@ -2,6 +2,7 @@
<html <html
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}" layout:decorate="~{layout/default_layout}"
> >
<th:block layout:fragment="head"> <th:block layout:fragment="head">
@ -38,13 +39,13 @@
<th:block layout:fragment="content" id="content"> <th:block layout:fragment="content" id="content">
<section class="wrapper style2"> <section class="wrapper style2">
<div class="container" th:if="${PERMISSION == 'OK'}" onclick="loadEditor()"> <div class="container" sec:authorize="isAuthenticated()" onclick="loadEditor()">
<header class="major"> <header class="major">
<h2 id="title_layer" th:text="${srcPost.title}">A gigantic heading you can use for whatever</h2> <h2 id="title_layer" th:text="${srcPost.title}">A gigantic heading you can use for whatever</h2>
<p th:text="${#temporals.format(T(java.time.Instant).ofEpochMilli(srcPost.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')}"></p> <p th:text="${#temporals.format(T(java.time.Instant).ofEpochMilli(srcPost.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')}"></p>
</header> </header>
</div> </div>
<div class="container" th:if="${PERMISSION != 'OK'}" onclick="openLoginPopup('login')"> <div class="container" sec:authorize="isAnonymous()" onclick="openLoginPopup('login')">
<header class="major"> <header class="major">
<h2 id="title_layer" th:text="${srcPost.title}">A gigantic heading you can use for whatever</h2> <h2 id="title_layer" th:text="${srcPost.title}">A gigantic heading you can use for whatever</h2>
<p th:text="${#temporals.format(T(java.time.Instant).ofEpochMilli(srcPost.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')}"></p> <p th:text="${#temporals.format(T(java.time.Instant).ofEpochMilli(srcPost.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')}"></p>

View File

@ -2,6 +2,7 @@
<html <html
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}" layout:decorate="~{layout/default_layout}"
> >
<th:block layout:fragment="head" id="head"> <th:block layout:fragment="head" id="head">
@ -100,10 +101,10 @@
</th:block> </th:block>
<th:block layout:fragment="content" id="content"> <th:block layout:fragment="content" id="content">
<div id="main_layer"> <div id="main_layer">
<th:block th:if="${PERMISSION != 'OK'}"> <th:block sec:authorize="isAnonymous()">
<h1>권한이 없는 뎁쇼?!</h1> <h1>권한이 없는 뎁쇼?!</h1>
</th:block> </th:block>
<th:block th:if="${PERMISSION == 'OK'}"> <th:block sec:authorize="isAuthenticated()">
<div class="layer"> <div class="layer">
<input id="title_field" class="write_option" /> <input id="title_field" class="write_option" />
</div> </div>

View File

@ -2,6 +2,7 @@
<html <html
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}"> layout:decorate="~{layout/default_layout}">
<th:block layout:fragment="head"> <th:block layout:fragment="head">
<script type="text/javascript" th:src="@{/js/blog.js}"></script> <script type="text/javascript" th:src="@{/js/blog.js}"></script>
@ -10,7 +11,7 @@
<script type="text/javascript" th:src="@{/js/test.js}"></script> <script type="text/javascript" th:src="@{/js/test.js}"></script>
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" /> <link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<script th:inline="javascript"> <script th:inline="javascript">
document.addEventListener("DOMContentLoaded", onLoaded); // document.addEventListener("DOMContentLoaded", onLoaded);
function goToViewer(item) { function goToViewer(item) {
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value; let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
location.href = uploadUrl location.href = uploadUrl
@ -73,12 +74,12 @@
</div> </div>
</section> </section>
<section class="col-4 col-12-narrower"> <section class="col-4 col-12-narrower">
<div class="box highlight" th:if="${PERMISSION == 'OK'}" onclick=gotoWrite()> <div class="box highlight" sec:authorize="isAuthenticated()" onclick=gotoWrite()>
<i class="icon solid major fa-pencil-alt"></i> <i class="icon solid major fa-pencil-alt"></i>
<h3>글쓰기[Writing]</h3> <h3>글쓰기[Writing]</h3>
<p>오직 주인장 만의 권한 임요. 그냥 내가 쓰기 편하게 여기 놔둔 메뉴임. 님들은 못씀요.<br>[Only the owner has the authority. This is just a menu that I put here for my convenience. You can't use it.]</p> <p>오직 주인장 만의 권한 임요. 그냥 내가 쓰기 편하게 여기 놔둔 메뉴임. 님들은 못씀요.<br>[Only the owner has the authority. This is just a menu that I put here for my convenience. You can't use it.]</p>
</div> </div>
<div class="box highlight" th:if="${PERMISSION != 'OK'}" onclick="openLoginPopup('login')"> <div class="box highlight" sec:authorize="isAnonymous()" onclick="openLoginPopup('login')">
<i class="icon solid major fa-pencil-alt"></i> <i class="icon solid major fa-pencil-alt"></i>
<h3>글쓰기[Writing]</h3> <h3>글쓰기[Writing]</h3>
<p>오직 주인장 만의 권한 임요. 그냥 내가 쓰기 편하게 여기 놔둔 메뉴임. 님들은 못씀요.<br>[Only the owner has the authority. This is just a menu that I put here for my convenience. You can't use it.]</p> <p>오직 주인장 만의 권한 임요. 그냥 내가 쓰기 편하게 여기 놔둔 메뉴임. 님들은 못씀요.<br>[Only the owner has the authority. This is just a menu that I put here for my convenience. You can't use it.]</p>

View File

@ -2,6 +2,7 @@
<html <html
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}"> layout:decorate="~{layout/default_layout}">
<th:block layout:fragment="head"> <th:block layout:fragment="head">
<script type="text/javascript" th:src="@{/js/blog.js}"></script> <script type="text/javascript" th:src="@{/js/blog.js}"></script>

View File

@ -2,6 +2,8 @@
<html <html
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}" layout:decorate="~{layout/default_layout}"
> >
<th:block layout:fragment="head"> <th:block layout:fragment="head">

View File

@ -2,6 +2,7 @@
<html <html
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}"> layout:decorate="~{layout/default_layout}">
<th:block layout:fragment="head"> <th:block layout:fragment="head">
<script type="text/javascript" th:src="@{/js/blog.js}"></script> <script type="text/javascript" th:src="@{/js/blog.js}"></script>

View File

@ -2,6 +2,7 @@
<html <html
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}"> layout:decorate="~{layout/default_layout}">
<th:block layout:fragment="head"> <th:block layout:fragment="head">
<link th:href="@{/css/private.css}" rel="stylesheet" /> <link th:href="@{/css/private.css}" rel="stylesheet" />

View File

@ -2,6 +2,7 @@
<html <html
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}"> layout:decorate="~{layout/default_layout}">
<th:block layout:fragment="head"> <th:block layout:fragment="head">
<script type="text/javascript" th:src="@{/js/blog.js}"></script> <script type="text/javascript" th:src="@{/js/blog.js}"></script>

View File

@ -2,6 +2,7 @@
<html <html
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}"> layout:decorate="~{layout/default_layout}">
<th:block layout:fragment="head"> <th:block layout:fragment="head">
<script type="text/javascript" th:src="@{/js/blog.js}"></script> <script type="text/javascript" th:src="@{/js/blog.js}"></script>

View File

@ -2,6 +2,7 @@
<html <html
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}" layout:decorate="~{layout/default_layout}"
> >
<th:block layout:fragment="head" id="head"> <th:block layout:fragment="head" id="head">

View File

@ -2,6 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" <html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}"> layout:decorate="~{layout/default_layout}">
<head> <head>

View File

@ -1,39 +1,54 @@
<!DOCTYPE html> <!DOCTYPE html>
<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"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<th:block th:fragment="header"> <th:block th:fragment="header">
<div id="header"> <div id="header">
<!-- Logo --> <!-- Logo -->
<h1><a th:href="@{/}" id="logo">BUM ' <em>sPace</em></a></h1> <h1><a th:href="@{/}" id="logo">BUM ' <em>sPace</em></a></h1>
<th:block sec:authorize="isAuthenticated()">
<span style="margin-left:20px;">환영합니다, <b sec:authentication="name"></b>님!</span>
</th:block>
<!-- Nav --> <!-- Nav -->
<nav id="nav"> <nav id="nav">
<ul> <ul>
<li id="menu_home" ><a th:href="@{/}">Home</a></li> <li id="menu_home" ><a th:href="@{/}">Home</a></li>
<li id="menu_posts"><a href="blog/posts">Posts</a></li> <li id="menu_posts"><a href="blog/posts">Posts</a></li>
<li id="menu_sec"><a href="left-sidebar">Left Sidebar</a></li> <!-- <li id="menu_sec"><a href="left-sidebar">Left Sidebar</a></li>-->
<li id="menu_thr"><a href="right-sidebar">Right Sidebar</a></li> <!-- <li id="menu_thr"><a href="right-sidebar">Right Sidebar</a></li>-->
<li id="menu_four"><a href="two-sidebar">Two Sidebar</a></li> <!-- <li id="menu_four"><a href="two-sidebar">Two Sidebar</a></li>-->
<li id="menu_drop"> <li id="menu_drop">
<a href="#">About</a> <a href="#">About</a>
<ul> <ul>
<li><a href="#">Lorem dolor</a></li> <li><a href="javascript:gotoWhere()">bums's where</a></li>
<li><a href="#">Magna phasellus</a></li> <li><a href="#">Magna phasellus</a></li>
<li><a href="#">Etiam sed tempus</a></li> <li><a href="#">Etiam sed tempus</a></li>
<li> <li>
<a href="#">Submenu</a> <a href="#">Submenu</a>
<ul> <ul>
<li><a href="#">Lorem dolor</a></li> <th:block sec:authorize="isAuthenticated()">
<li><a href="javascript:gotoWrite()">글쓰기</a></li>
<li><a href="javascript:gotoModify()">수정하기</a></li>
</th:block>
<li><a href="#">Phasellus magna</a></li> <li><a href="#">Phasellus magna</a></li>
<li><a href="#">Magna phasellus</a></li> <!-- <li><a href="#">Magna phasellus</a></li>-->
<li><a href="#">Etiam nisl</a></li> <!-- <li><a href="#">Etiam nisl</a></li>-->
<li><a href="#">Veroeros feugiat</a></li> <!-- <li><a href="#">Veroeros feugiat</a></li>-->
</ul> </ul>
</li> </li>
<li><a th:href="@{/licenses}">Licenses</a></li> <li><a th:href="@{/licenses}">Licenses</a></li>
</ul> </ul>
</li> </li>
<th:block sec:authorize="!isAuthenticated()">
<li id="menu_login" ><a href="javascript:openLoginPopup('login')">LOGIN</a></li>
</th:block>
<th:block sec:authorize="isAuthenticated()">
<li>
<a href="javascript:logout()" style="margin-left:10px;">로그아웃</a>
<li>
</th:block>
</ul> </ul>
</nav> </nav>

View File

@ -13,5 +13,7 @@
<link rel="stylesheet" href="assets/css/main.css" /> <link rel="stylesheet" href="assets/css/main.css" />
<script async th:src="@{https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9504446465764716}" crossorigin="anonymous"></script> <script async th:src="@{https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9504446465764716}" crossorigin="anonymous"></script>
<script type="text/javascript" th:src="@{/js/common.js}"></script> <script type="text/javascript" th:src="@{/js/common.js}"></script>
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_parameter" th:content="${_csrf.parameterName}"/>
</th:block> </th:block>
</html> </html>

View File

@ -2,6 +2,7 @@
<html lagn="ko" <html lagn="ko"
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
xmlns="http://www.w3.org/1999/html"> xmlns="http://www.w3.org/1999/html">
<head> <head>
<base th:href="@{/}" /> <base th:href="@{/}" />
@ -20,12 +21,47 @@
<th:block th:replace="~{fragments/footer :: footer}"></th:block> <th:block th:replace="~{fragments/footer :: footer}"></th:block>
</div> </div>
<div id="overlay" class="login_overlay"> <div id="overlay" class="login_overlay">
<style>
.custom-checkbox {
display: none; /* 실제 체크박스 숨김 */
}
.custom-label {
display: inline-block;
width: 20px;
height: 20px;
border: 2px solid #555;
cursor: pointer;
vertical-align: middle;
position: relative;
}
.custom-checkbox:checked + .custom-label {
background-color: #007bff;
border-color: #007bff;
}
.custom-checkbox:checked + .custom-label::after {
content: '';
position: absolute;
top: 3px;
left: 7px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
</style>
<div id="popup" class="login_popup"> <div id="popup" class="login_popup">
<div id="loginForm" class="login_form"> <div id="loginForm" class="login_form">
<h2>로그인</h2> <h2>로그인</h2>
<form id="loginFormElement" onsubmit="return false;"> <form id="loginFormElement" onsubmit="return false;">
<input type="text" th:data="${enc}" id="loginId" placeholder="아이디" required> <input type="text" th:data="${enc}" id="loginId" placeholder="아이디" required/>
<input type="password" th:data="${type}" id="loginPassword" placeholder="비밀번호" required> <input type="password" th:data="${type}" id="loginPassword" placeholder="비밀번호" required/>
<input type="checkbox" id="rememberMe" class="custom-checkbox"/>
<label for="rememberMe" class="custom-label"></label>
<span>자동로그인</span>
<button type="submit" class="button">로그인</button> <button type="submit" class="button">로그인</button>
</form> </form>
</div> </div>