2024-06-05T01:42:18.294+09:00 ERROR 10044 --- [nio-8080-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
java.lang.NullPointerException: null
at org.example.domain.infra.jwt.JwtAuthenticationFilter.doFilterInternal(JwtAuthenticationFilter.kt:40) ~[main/:na]
로그인을 한뒤에 게시글을 작성하려고 하면 이것이 뜨게 된다.
그이유는
package org.example.domain.infra.jwt
import io.jsonwebtoken.Claims
import io.jsonwebtoken.Jws
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.security.Keys
import jakarta.servlet.http.Cookie // javax가 아닌 jakarta를 사용
import jakarta.servlet.http.HttpServletResponse
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import java.nio.charset.StandardCharsets
import java.time.Duration
import java.time.Instant
import java.util.Date
@Component
class JwtPlugin(
@Value("\${auth.jwt.issuer}") private val issuer: String,
@Value("\${auth.jwt.secret}") private val secret: String,
@Value("\${auth.jwt.accessTokenExpirationHour}") private val accessTokenExpirationHour: Long,
) {
fun validateToken(jwt: String): Result<Jws<Claims>> {
return kotlin.runCatching {
val key = Keys.hmacShaKeyFor(secret.toByteArray(StandardCharsets.UTF_8))
Jwts.parser().verifyWith(key).build().parseSignedClaims(jwt)
}
}
fun generateAccessTokenForSocialUser(
subject: String,
nickname: String,
httpServletResponse: HttpServletResponse,
): String {
val token = generateToken(subject, nickname, Duration.ofHours(accessTokenExpirationHour))
addTokenToCookie(token, httpServletResponse)
return token
}
fun generateAccessToken(
subject: String,
nickname: String,
): String {
return generateToken(subject, nickname, Duration.ofHours(accessTokenExpirationHour))
}
private fun generateToken(
subject: String,
nickname: String,
expirationPeriod: Duration,
): String {
val claims: Claims =
Jwts.claims()
.add(mapOf( "nickname" to nickname))
.build()
val key = Keys.hmacShaKeyFor(secret.toByteArray(StandardCharsets.UTF_8))
val now = Instant.now()
return Jwts.builder()
.subject(subject)
.issuer(issuer)
.issuedAt(Date.from(now))
.expiration(Date.from(now.plus(expirationPeriod)))
.claims(claims)
.signWith(key)
.compact()
}
private fun addTokenToCookie(
token: String,
httpServletResponse: HttpServletResponse,
) {
val cookie = Cookie("jwt_token", token)
cookie.isHttpOnly = false // JavaScript 에서 쿠키에 접근하지 못하도록 설정
cookie.maxAge = (accessTokenExpirationHour * 60 * 60 * 24 * 7).toInt() // 약 8.4일
cookie.path = "/" // 쿠키 객체를 모든 경로에서 쓸 수 있게 설정함.
httpServletResponse.addCookie(cookie) // 쿠키 객체를 리스폰스에 담아서 클라이언트에게 주기.
}
}
이걸 보면 40번째 줄에 val userPrincipal = UserPrincipal(id = userId, email = email)이 있는데, 내가 소셜로그인을 위해 이메일 부분을 제거했는데 이메일을 받게 되어 있어서 그런것이다.
if (jwt != null) {
jwtPlugin.validateToken(jwt)
.onSuccess {
val userId = it.payload.subject.toLong()
// val email = it.payload.get("email", String::class.java)
val nickname = it.payload.get("nickname", String::class.java) // 이메일 대신 닉네임 사용
val userPrincipal = UserPrincipal(id = userId, nickname = nickname)// 마찬가지 닉네임
val details = WebAuthenticationDetailsSource().buildDetails(request)
val auth = JwtAuthenticationToken(userPrincipal, details)
SecurityContextHolder.getContext().authentication = auth
}
}
이렇게 이메일을 닉네임으로 바꾸고
data class UserPrincipal(
val id: Long,
val nickname: String,
)
이렇게 이메일을 없애고 닉네임만으로 한정한다.
그리고 게시글을 유저프린시팔로 로그인 한사람만 작성가능, 쓴 사람만 슈정, 삭제기능을 만드는 중이다.