ㅇㅇ 로그인아웃 방식 변경

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 27012
#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","-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

View File

@ -50,6 +50,8 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity6")
implementation("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect")
implementation ("org.jsoup:jsoup:1.18.1")
@ -104,6 +106,7 @@ tasks.withType<Test> {
tasks.jar {
archiveFileName.set("app.jar")
manifest {
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.Configuration
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.WebMvcConfigurer
import java.time.Duration
@ -28,10 +30,13 @@ class AppConfig : WebMvcConfigurer {
registry.addResourceHandler(resourceHandler).addResourceLocations(resourceLocation).setCacheControl(cacheControl)
}
override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(authInterceptor())
super.addInterceptors(registry)
}
@Bean
fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder()
// override fun addInterceptors(registry: InterceptorRegistry) {
// registry.addInterceptor(authInterceptor())
// .addPathPatterns("**/*.bs", "**/*.bjx")
// super.addInterceptors(registry)
// }
// @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.EncTypeKey
import kr.lunaticbum.back.lun.model.UserManager
import kr.lunaticbum.back.lun.service.JwtService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.lang.Nullable
import org.springframework.security.web.authentication.RememberMeServices
import org.springframework.stereotype.Component
import org.springframework.stereotype.Service
import org.springframework.web.servlet.HandlerInterceptor
@ -19,12 +19,8 @@ import org.springframework.web.servlet.ModelAndView
@Component
class BumsInterceptor : HandlerInterceptor {
@Autowired
lateinit var jwtService : JwtService
@Autowired
lateinit var globalEvv : GlobalEnvironment
@Autowired
lateinit var userManager: UserManager
val WRITE_PERMISSION_KEY = "PERMISSION"
@ -36,9 +32,13 @@ class BumsInterceptor : HandlerInterceptor {
// println("==================== BEGIN ====================")
// println("Request URL ===> " + request.requestURL)
// }
return super.preHandle(request, response, handler)
}
// @Autowired
// lateinit var rememberMeServices: RememberMeServices
@Throws(Exception::class)
override fun postHandle(
@ -47,68 +47,9 @@ class BumsInterceptor : HandlerInterceptor {
handler: Any,
@Nullable modelAndView: ModelAndView?
) {
var skippResourcesExtension = arrayListOf(".ajax",".js",".css","/tlg/",".api","error").filter { request.requestURI.contains(it)}.size > 0
if (!skippResourcesExtension) {
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("===============================================")
}
// if(remeberMe && authResult != null) {
// rememberMeServices.loginSuccess(httpServletRequest, responce, authResult)
// }
super.postHandle(request, response, handler, modelAndView)
}

View File

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

View File

@ -3,6 +3,7 @@ package kr.lunaticbum.back.lun.configs
import com.fasterxml.jackson.databind.ObjectMapper
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import kr.lunaticbum.back.lun.model.UserManager
import kr.lunaticbum.back.lun.utils.LogService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
@ -11,6 +12,8 @@ import org.springframework.http.HttpMethod
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
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.configuration.EnableWebSecurity
import org.springframework.security.config.http.SessionCreationPolicy
@ -24,60 +27,87 @@ import org.springframework.web.ErrorResponse
@Configuration
@EnableWebSecurity
class SecurityConfig {
class SecurityConfig(
private val userManager: UserManager,
private val bCryptPasswordEncoder: BCryptPasswordEncoder
) {
@Autowired
lateinit var logService: LogService
@Bean
fun filterChain(http: HttpSecurity): SecurityFilterChain {
http.csrf {
it.ignoringRequestMatchers("/user/joinUser.ajax").disable()
http.csrf { csrf ->
csrf.ignoringRequestMatchers("/user/login.bjx", "/user/joinUser.bjx") // 여기 예외 추가
}.authorizeHttpRequests { auth ->
auth
.requestMatchers(
"/", "/home",
"/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.cors { it.disable() }
http.headers {
it.frameOptions { frameOptionsConfig ->
frameOptionsConfig.disable()
}
}
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()
}
@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 =
AuthenticationEntryPoint { request: HttpServletRequest?, response: HttpServletResponse, authException: AuthenticationException? ->
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.EncTypeKey
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.getFileExtension
import net.coobird.thumbnailator.Thumbnails
@ -28,6 +27,8 @@ import org.springframework.core.io.Resource
import org.springframework.core.io.UrlResource
import org.springframework.http.MediaType
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.multipart.MultipartFile
import org.springframework.web.reactive.function.client.WebClient
@ -55,7 +56,7 @@ class BlogController() {
@Autowired
lateinit var logService: LogService
val WRITE_PERMISSION_KEY = "PERMISSION"
@GetMapping("write/{token}","write")
@GetMapping("write/{token}","write.bs")
fun writ(@PathVariable token : String? ) : ResultMV{
val vm = ResultMV("content/blog/write")
if (token.equals(TEMPTOKEN)) {
@ -67,17 +68,10 @@ class BlogController() {
} else {
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
}
@PostMapping("post.ajax")
@PostMapping("post.bjx")
fun post(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity<ResponceResult> {
logService.log(httpServletRequest.requestURI)
logService.log(jsonString)
@ -164,16 +158,16 @@ class BlogController() {
return vm
}
@Autowired
lateinit var jwtService : JwtService
@GetMapping("modify")
@GetMapping("modify.bs")
fun modify(httpServletRequest: HttpServletRequest, @RequestParam("token") token : String?) : ResultMV{
logService.log("incoming modify")
val vm = ResultMV("content/blog/modify")
vm.modelMap.put(WRITE_PERMISSION_KEY,"NO")
httpServletRequest.getSession(true)?.let { session ->
(session.getAttribute(WRITE_PERMISSION_KEY) as? Boolean)?.let {
val authentication = SecurityContextHolder.getContext().authentication
val principal = authentication.principal
if (principal is UserDetails) {
val username = principal.username
// 추가 정보 사용 가능
postManageg.find20()?.apply {
forEach {
it.title = URLDecoder.decode(it.title)
@ -186,7 +180,6 @@ class BlogController() {
vm.modelMap.put("path","editor/")
vm.modelMap.put("SK",token)
}
}
vm.modelMap.put("rowKey","chunkedPosts_")
return vm
}

View File

@ -30,7 +30,7 @@ class BumsPrivate {
@Autowired
lateinit var locationService: LocationLogService
@GetMapping("where")
@GetMapping("where.bs")
fun where() : ResultMV {
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.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
@ -17,12 +16,18 @@ 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.authentication.AuthenticationManager
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.web.bind.annotation.*
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.util.*
import javax.naming.AuthenticationException
@RestController
@ -38,10 +43,8 @@ class UserController {
@Autowired
lateinit var userManager: UserManager
@Autowired
lateinit var jwtService : JwtService
@GetMapping("join")
@GetMapping("join.bs")
fun hello(httpServletRequest: HttpServletRequest): ResultMV {
logService.log("onJoin")
val vm = ResultMV("content/user/join")
@ -60,7 +63,7 @@ class UserController {
return vm
}
@GetMapping("login")
@GetMapping("login.bs")
fun userLogin(httpServletRequest: HttpServletRequest): ResultMV {
logService.log("onJoin")
val vm = ResultMV("content/user/login")
@ -69,9 +72,18 @@ class UserController {
return vm
}
// @GetMapping("logout.bs")
// fun logoutBs(session: HttpSession): ResultMV {
// session.invalidate(); // 세션 날리기 (로그아웃)
// return "redirect:/"; // 홈으로 이동
// }
@Autowired
lateinit var authenticationManager: AuthenticationManager
@ResponseBody
@PostMapping("login.ajax")
@PostMapping("login.bjx")
fun login(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity<LoginResult> {
try {
@ -82,24 +94,37 @@ class UserController {
var u : UserDetails? = null
var user : User? = null
var tokenData : TokenData? = null
var remeberMe = false
var authResult : Authentication? = null
jsonString.extractModelData { exception, originDataString ->
if (exception == null) {
logService.log(originDataString)
logService.log("originDataString >>> $originDataString")
val target = Gson().fromJson(originDataString, User::class.java) ?: User()
user = userManager.findById(target.user_id?.trim() ?: "")?.block()
if (user == null && ((target.user_id?.trim()?.length ?: 0) > 3 == true)) {
user = userManager.findByEmail(target.user_id?.trim() ?: "")?.block()
}
if (user != null) {
if(userManager.isCorrectUser(user!!,target.user_pw!!)){
tokenData = jwtService.generate(user!!)
try {
val authToken = UsernamePasswordAuthenticationToken(target.user_id, target.user_pw)
authResult = authenticationManager.authenticate(authToken) // 인증 시도
println("authResult >>>> $authResult")
// 인증 성공 시 SecurityContextHolder에 인증 정보 저장
SecurityContextHolder.getContext().authentication = authResult
// 인증 정보가 담긴 SecurityContext를 세션에 저장
val session = httpServletRequest.getSession(true)
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext())
println("authResult >>>> LOGIN OK")
val principal = authResult?.principal
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
}
} else {
lResultMsg = "not founding user[can't find same id,email.. ]"
lResultCode = 7100
} catch (e: Exception) {
e.printStackTrace()
}
} else {
exception.printStackTrace()
@ -117,7 +142,6 @@ class UserController {
this.refresh = setTokenToCookie(JwtRule.REFRESH_PREFIX.value, tokenData?.refreshToken ?: "", globalEvv.REFRESH_EXPIRATION / 1000).toString().replace("refresh=","")
}).apply {
}
return responce
@ -149,7 +173,7 @@ class UserController {
}
@ResponseBody
@PostMapping("logout.ajax")
@PostMapping("logout.bjx")
fun logout(httpServletRequest: HttpServletRequest) : ResponseEntity<ResponceResult> {
val responce = ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(ResponceResult().apply {
@ -158,7 +182,7 @@ class UserController {
}
@ResponseBody
@PostMapping("joinUser.ajax")
@PostMapping("joinUser.bjx")
fun joinUser(httpServletRequest: HttpServletRequest, @RequestBody jsonString: String) : ResponseEntity<ResponceResult> {
logService.log("${httpServletRequest.requestURI}")
logService.log(jsonString)
@ -174,9 +198,6 @@ class UserController {
}else if (userManager.findById(user!!.user_id!!)?.block() != null) {
lResultCode = 7001
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 {
u = userManager.save(user).block()
}

View File

@ -39,10 +39,12 @@ class User {
@CreatedDate
var user_join: Long = 0L
var user_name: String? = null
// var user_name: String? = null
var isAccept : String? = null
var isAdmin : String? = null
var remeberMe : Boolean? = false
fun checkValid() : Boolean {
if (
((user_id?.length ?: 0) > 5) &&
@ -111,8 +113,8 @@ interface UserRepository : ReactiveMongoRepository<User, String> {
@Query("{user_id :?0}")
override fun findById(user_id: String): Mono<User>
@Query("{user_email :?0}")
fun findByEmail(user_email: String): Mono<User>
// @Query("{user_email :?0}")
// fun findByEmail(user_email: String): Mono<User>
fun save(user: User): Mono<User>
@ -123,23 +125,24 @@ enum class UserRole {
}
interface UserService {
fun findById(id: String): Mono<User>?
fun findByEmail(id: String): Mono<User>?
// fun findByEmail(id: String): Mono<User>?
}
@Service
class UserManager : UserService , UserDetailsService {
class UserManager(
private val passwordEncoder: PasswordEncoder
) : UserService , UserDetailsService {
@Autowired
private lateinit var logService: LogService
@Autowired
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>? {
return userRepository.findById(id)
@ -149,19 +152,22 @@ class UserManager : UserService , UserDetailsService {
fun save(user: User): Mono<User> {
println("saved user before ${user}")
user.hashPassword(bCryptPasswordEncoder)
user.hashPassword(passwordEncoder)
return userRepository.save(user)
}
fun isCorrectUser(user: User, password : String) : Boolean {
return user.checkPassword(password,bCryptPasswordEncoder)
return user.checkPassword(password,passwordEncoder)
}
override fun loadUserByUsername(username: String?): UserDetails {
var user = findById(username!!)?.blockOptional(Duration.ofMillis(5000L))?.get() ?: User()
logService.log("username ${username}")
user.hashPassword(bCryptPasswordEncoder)
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()
var user = findById(username!!)?.blockOptional(Duration.ofMillis(5000L))?.get() ?: User()
// 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
import io.jsonwebtoken.Claims
import io.jsonwebtoken.Jws
import io.jsonwebtoken.Jwts
import jakarta.servlet.http.Cookie
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 {
try {
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
} catch (e :Exception){
}
return false
}
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)
}
fun getUserIdFromToken(token: String?): String? {
try {
return jwtUtil.extractToken(token,jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY))?.body?.get("Identifier")
.toString()
} catch (e: Exception) {
return null
}
}
fun getUserIdFromRefresh(token: String?): String? {
try {
return jwtUtil.extractToken(token,jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY))?.body?.get("Identifier")
.toString()
} catch (e: Exception) {
return null
}
}
fun hasPerrmission(request: HttpServletRequest): Boolean {
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) {
var access : Cookie?= null
var refresh : Cookie?= null
request.cookies.forEach {
if (it.name.equals("access", true) && validateAccessToken(it.value)){
access = it
correctUserCheck += 1
}
}
request.cookies.forEach {
if (it.name.equals("refresh", true) && validateRefreshToken(access?.value,it.value)){
refresh = it
correctUserCheck += 1
}
}
if (correctUserCheck > 0) {
println("Response correctUserCheck ===> ${correctUserCheck}")
} else {
println("Response correctUserCheck ===> ${correctUserCheck}")
}
} else if (request.requestURI.contains("logout")) {
}
return correctUserCheck > 0
}
}
//package kr.lunaticbum.back.lun.service
//
//import io.jsonwebtoken.Claims
//import io.jsonwebtoken.Jws
//import io.jsonwebtoken.Jwts
//import jakarta.servlet.http.Cookie
//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 {
// try {
// 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
// } catch (e :Exception){
//
// }
// return false
// }
//
// 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)
// }
// fun getUserIdFromToken(token: String?): String? {
// try {
// return jwtUtil.extractToken(token,jwtUtil.getSigningKey(globalEvv.ACCESS_SECRET_KEY))?.body?.get("Identifier")
// .toString()
// } catch (e: Exception) {
// return null
// }
// }
// fun getUserIdFromRefresh(token: String?): String? {
// try {
// return jwtUtil.extractToken(token,jwtUtil.getSigningKey(globalEvv.REFRESH_SECRET_KEY))?.body?.get("Identifier")
// .toString()
// } catch (e: Exception) {
// return null
// }
// }
//
//
//
// fun hasPerrmission(request: HttpServletRequest): Boolean {
// 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) {
// var access : Cookie?= null
// var refresh : Cookie?= null
// request.cookies.forEach {
// if (it.name.equals("access", true) && validateAccessToken(it.value)){
// access = it
// correctUserCheck += 1
// }
// }
// request.cookies.forEach {
// if (it.name.equals("refresh", true) && validateRefreshToken(access?.value,it.value)){
// refresh = it
// correctUserCheck += 1
// }
// }
// if (correctUserCheck > 0) {
// println("Response correctUserCheck ===> ${correctUserCheck}")
// } else {
// println("Response correctUserCheck ===> ${correctUserCheck}")
// }
// } else if (request.requestURI.contains("logout")) {
//
// }
// 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 ->
model.data?.let { jsonString ->
try {
println("RequestModel ${jsonString}")
calback.invoke(null,model.extractData())
} catch (e: Exception) {
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
resource.handler=.
resource.location=.
server.forward-headers-strategy=framework
#>>>>>>> ab915d0a416c69708f1df1ad76d7a14c779c1f59

View File

@ -168,3 +168,31 @@ footer {
min-height: 5vh;
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.firstPostLon = encodeURIComponent(currentLon)
}
let uploadUrl = getMainPath() + "/blog/post.ajax";
let uploadUrl = getMainPath() + "/blog/post.bjx";
if(confirm(JSON.stringify(baseData) + "\n해당 내용으로\n유저 등록 하실??")) {
post(uploadUrl,type,JSON.stringify(baseData),keyword, function (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.setRequestHeader("Content-Type", "text/plain");
var odd = []
@ -172,15 +172,15 @@ function mainPath() {
}
function gotoWrite() {
document.location.replace(getMainPath()+"/blog/write")
document.location.replace(getMainPath()+"/blog/write.bs")
}
function gotoModify() {
document.location.replace(getMainPath()+"/blog/modify")
document.location.replace(getMainPath()+"/blog/modify.bs")
}
function gotoWhere() {
document.location.replace(getMainPath()+"/bums/where")
document.location.replace(getMainPath()+"/bums/where.bs")
}
function logout() {
@ -191,21 +191,35 @@ function logout() {
console.log(document.cookie["JSESSIONID"])
document.cookie = "JSESSIONID=; expires=Thu, 01 Jan 1970 00:00:01 GMT;"
document.cookie = "CLEAR="+Date.now()+"";
let logOutUrl = getMainPath() + "/user/logout.ajax";
post(logOutUrl,"","","", function (resultData) {
alert("로그아웃 됨요~! 빠염~!")
document.location.replace(document.location)
})
let logOutUrl = getMainPath() + "/user/logout.bs";
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() {
console.log(`location.port >> ${location.port}`)
location.href = getMainPath()+"/login"
location.href = getMainPath()+"/login.bs"
}
function gotoJoin() {
document.location.replace(getMainPath() + "/user/join")
document.location.replace(getMainPath() + "/user/join.bs")
}
function goToView(path,id) {
@ -218,7 +232,7 @@ function onclickLogin(type, keyword) {
'user_id': user_id.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) {
document.cookie = "access=" + data.token.split(";")[0]+";"
@ -229,7 +243,7 @@ function onclickLogin(type, keyword) {
} else {
if (data.resultCode === 7100) {
if(confirm(`너 누구임 정보 없는데?!\n${data.resultMsg}[${data.resultCode}]\n가입 할래!?`)){
document.location.replace(getMainPath() + "/user/join")
document.location.replace(getMainPath() + "/user/join.bs")
}
} else {
alert(`너 누구임?!\n${data.resultMsg}[${data.resultCode}]`)
@ -320,11 +334,13 @@ function submitLoginForm() {
// const password = document.getElementById('loginPassword').value;
let user_id = document.getElementById('loginId')
let user_pw = document.getElementById('loginPassword')
let rememberMe = document.getElementById('rememberMe')
let data = {
'user_id': user_id.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()
if (data.isOk) {
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(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)
})
} else {

View File

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

View File

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

View File

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

View File

@ -2,6 +2,7 @@
<html
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}"
>
<th:block layout:fragment="head">
@ -38,13 +39,13 @@
<th:block layout:fragment="content" id="content">
<section class="wrapper style2">
<div class="container" th:if="${PERMISSION == 'OK'}" onclick="loadEditor()">
<div class="container" sec:authorize="isAuthenticated()" onclick="loadEditor()">
<header class="major">
<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>
</header>
</div>
<div class="container" th:if="${PERMISSION != 'OK'}" onclick="openLoginPopup('login')">
<div class="container" sec:authorize="isAnonymous()" onclick="openLoginPopup('login')">
<header class="major">
<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>

View File

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

View File

@ -2,6 +2,7 @@
<html
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
layout:decorate="~{layout/default_layout}">
<th:block layout:fragment="head">
<script type="text/javascript" th:src="@{/js/blog.js}"></script>
@ -10,7 +11,7 @@
<script type="text/javascript" th:src="@{/js/test.js}"></script>
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<script th:inline="javascript">
document.addEventListener("DOMContentLoaded", onLoaded);
// document.addEventListener("DOMContentLoaded", onLoaded);
function goToViewer(item) {
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
location.href = uploadUrl
@ -73,12 +74,12 @@
</div>
</section>
<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>
<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>
</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>
<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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,39 +1,54 @@
<!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">
<div id="header">
<!-- Logo -->
<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 id="nav">
<ul>
<li id="menu_home" ><a th:href="@{/}">Home</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_thr"><a href="right-sidebar">Right Sidebar</a></li>
<li id="menu_four"><a href="two-sidebar">Two 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_four"><a href="two-sidebar">Two Sidebar</a></li>-->
<li id="menu_drop">
<a href="#">About</a>
<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="#">Etiam sed tempus</a></li>
<li>
<a href="#">Submenu</a>
<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="#">Magna phasellus</a></li>
<li><a href="#">Etiam nisl</a></li>
<li><a href="#">Veroeros feugiat</a></li>
<!-- <li><a href="#">Magna phasellus</a></li>-->
<!-- <li><a href="#">Etiam nisl</a></li>-->
<!-- <li><a href="#">Veroeros feugiat</a></li>-->
</ul>
</li>
<li><a th:href="@{/licenses}">Licenses</a></li>
</ul>
</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>
</nav>

View File

@ -13,5 +13,7 @@
<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 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>
</html>

View File

@ -2,6 +2,7 @@
<html lagn="ko"
xmlns:th="http://www.thymeleaf.org"
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">
<head>
<base th:href="@{/}" />
@ -20,12 +21,47 @@
<th:block th:replace="~{fragments/footer :: footer}"></th:block>
</div>
<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="loginForm" class="login_form">
<h2>로그인</h2>
<form id="loginFormElement" onsubmit="return false;">
<input type="text" th:data="${enc}" id="loginId" placeholder="아이디" required>
<input type="password" th:data="${type}" id="loginPassword" placeholder="비밀번호" required>
<input type="text" th:data="${enc}" id="loginId" 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>
</form>
</div>