refactor(security): migrate from jws to jwe
Signed-off-by: Alan Brault <alan.brault@visus.io>
This commit is contained in:
+6
-6
@@ -12,12 +12,12 @@
|
||||
MONGODB_URI=mongodb://localhost:27017/kotlin-api
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════════
|
||||
# JWT Security Configuration
|
||||
# JWT Security Configuration (JWE - JSON Web Encryption)
|
||||
# ════════════════════════════════════════════════════════════════════════════════
|
||||
# REQUIRED: Generate a secure base64-encoded secret for JWT token signing
|
||||
# MUST be base64-encoded (your code uses Decoders.BASE64.decode)
|
||||
# REQUIRED: Generate a secure base64-encoded secret for JWT token encryption
|
||||
# MUST be exactly 32 bytes when base64-decoded (for AES-256-GCM encryption)
|
||||
#
|
||||
# Generate a secure base64-encoded secret (single line, no newlines):
|
||||
# openssl rand -base64 64 | tr -d '\n'
|
||||
# Generate a secure 32-byte base64-encoded encryption secret (single line, no newlines):
|
||||
# openssl rand -base64 32 | tr -d '\n'
|
||||
#
|
||||
JWT_SECRET=your-base64-encoded-secret-here
|
||||
JWT_ENCRYPTION_SECRET=your-base64-encoded-32-byte-secret-here
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.springframework.stereotype.Component
|
||||
@Component
|
||||
@ConfigurationProperties("jwt")
|
||||
data class JwtProperties(
|
||||
var secret: String = "",
|
||||
var encryptionSecret: String = "",
|
||||
var accessTokenExpiration: Long = 3600000, // 1 hour
|
||||
var refreshTokenExpiration: Long = 2592000000, // 30 days
|
||||
var issuer: String = "kotlin-api",
|
||||
|
||||
+10
-13
@@ -7,16 +7,15 @@ import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.MalformedJwtException
|
||||
import io.jsonwebtoken.UnsupportedJwtException
|
||||
import io.jsonwebtoken.io.Decoders
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import io.visus.demos.kotlinapi.config.JwtProperties
|
||||
import io.visus.demos.kotlinapi.domain.model.JwtTokenType
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.security.core.userdetails.UserDetails
|
||||
import org.springframework.stereotype.Component
|
||||
import java.security.SignatureException
|
||||
import java.util.Date
|
||||
import java.util.UUID
|
||||
import javax.crypto.SecretKey
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
@Component
|
||||
class JwtTokenProvider(
|
||||
@@ -24,9 +23,9 @@ class JwtTokenProvider(
|
||||
) {
|
||||
private val logger = LoggerFactory.getLogger(JwtTokenProvider::class.java)
|
||||
|
||||
private val secretKey: SecretKey by lazy {
|
||||
val bytes = Decoders.BASE64.decode(jwtProperties.secret)
|
||||
Keys.hmacShaKeyFor(bytes)
|
||||
private val encryptionKey: SecretKey by lazy {
|
||||
val bytes = Decoders.BASE64.decode(jwtProperties.encryptionSecret)
|
||||
SecretKeySpec(bytes, "AES")
|
||||
}
|
||||
|
||||
fun <T> extractClaim(
|
||||
@@ -36,9 +35,9 @@ class JwtTokenProvider(
|
||||
val claims =
|
||||
Jwts
|
||||
.parser()
|
||||
.verifyWith(secretKey)
|
||||
.decryptWith(encryptionKey)
|
||||
.build()
|
||||
.parseSignedClaims(token)
|
||||
.parseEncryptedClaims(token)
|
||||
.payload
|
||||
|
||||
return claimsResolver(claims)
|
||||
@@ -76,7 +75,7 @@ class JwtTokenProvider(
|
||||
).issuedAt(now)
|
||||
.expiration(expiry)
|
||||
.issuer(jwtProperties.issuer)
|
||||
.signWith(secretKey, Jwts.SIG.HS512)
|
||||
.encryptWith(encryptionKey, Jwts.KEY.DIRECT, Jwts.ENC.A256GCM)
|
||||
.compact()
|
||||
}
|
||||
|
||||
@@ -95,7 +94,7 @@ class JwtTokenProvider(
|
||||
.toString(), // UUIDv7
|
||||
).issuedAt(now)
|
||||
.expiration(expiry)
|
||||
.signWith(secretKey, Jwts.SIG.HS512)
|
||||
.encryptWith(encryptionKey, Jwts.KEY.DIRECT, Jwts.ENC.A256GCM)
|
||||
.compact()
|
||||
}
|
||||
|
||||
@@ -122,13 +121,11 @@ class JwtTokenProvider(
|
||||
try {
|
||||
Jwts
|
||||
.parser()
|
||||
.verifyWith(secretKey)
|
||||
.decryptWith(encryptionKey)
|
||||
.build()
|
||||
.parseSignedClaims(token)
|
||||
.parseEncryptedClaims(token)
|
||||
|
||||
return true
|
||||
} catch (ex: SignatureException) {
|
||||
logger.error("Invalid JWT signature: {}", ex.message)
|
||||
} catch (ex: MalformedJwtException) {
|
||||
logger.error("Invalid JWT token: {}", ex.message)
|
||||
} catch (ex: ExpiredJwtException) {
|
||||
|
||||
@@ -8,7 +8,7 @@ spring:
|
||||
uri: ${MONGODB_URI:mongodb://localhost:27017/kotlin-api}
|
||||
|
||||
jwt:
|
||||
secret: ${JWT_SECRET}
|
||||
encryption-secret: ${JWT_ENCRYPTION_SECRET}
|
||||
access-token-expiration: 3600000 # 1 hour in milliseconds
|
||||
refresh-token-expiration: 2592000000 # 30 days in milliseconds
|
||||
issuer: kotlin-api
|
||||
|
||||
Reference in New Issue
Block a user