JWT는 Json Web Token의 약자로 client-server 간의 통신에서 인증, 인가를 위해 사용되는 토큰이다.
웹 상에서 정보를 Json 형태로 받기 위해 안전한 암호화 방식 중 하나를 택하여 인코딩하여 생성한 토큰으로 주고받는다.
JWT 구성요소
- Header
- JWT임을 명시
- 사용된 암호화 알고리즘
- Payload
- 토큰에 담을 정보
- Signature
- 암호화 알고리즘( ex-> BASE64(Header) + BASE64(Payload) + 암호화 키)
- 발급한 사람
- 발급한 시간
- 발급자 확정용 데이터
- 시그니처만 암호화 됨
JWT의 특징은 내부 정보를 단순 BASE64 방식으로 인코딩하기 때문에 외부에서 쉽게 디코딩 할 수 있다.
외부에서 열람해도 되는 정보를 담아야하며, 이는 토큰 자체의 발급처를 확인하기 위해서 사용한다
이러한 방식은 우리 실생활에서 사용하는 지폐와 동일하다. 지폐는 외부에서 그 금액을 확인하고 금방 외형을 따라 만들 수 있지만, 발급처에 대한 보장 및 검증(인증,인가)은 확실하게 해야하는 경우에 사용한다.
따라서 토큰 내부에 비밀번호와 같은 값 입력이 금지된다
JWT 암호화 방식
- 암호화 종류 -> 선택해서 암호화 가능
- 양방향(대칭키, 비대칭티)
- 단방향
다음은 현재 내가 프로젝트에서 하고 있는 부분의 일부를 발췌한 코드이다.
@Component
class JwtUtil(
@Value("\${spring.jwt.secret}") private val secret: String, private val redisTemplate: RedisTemplate<String, String>) {
val secretKey: SecretKey by lazy { SecretKeySpec(secret.toByteArray(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().algorithm) }
fun getUsername(token: String?): String {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get<String>("memberName", String::class.java)
}
fun getRole(token: String?): String {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get<String>("role", String::class.java)
}
fun isExpired(token: String?): Boolean {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(Date())
}
fun getCategory(token: String?): String {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get<String>("category", String::class.java)
}
fun getJoin(token: String?): Boolean {
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get<Boolean>("join", Boolean::class.java)
}
fun createJwt(category: String, memberName: String, role: String, expiredMs: Long, join: Boolean): String {
return Jwts.builder()
.claim("category", category)
.claim("memberName", memberName)
.claim("role", role)
.claim("join", join)
.issuedAt(Date(System.currentTimeMillis()))
.expiration(Date(System.currentTimeMillis() + expiredMs))
.signWith(secretKey)
.compact()
}
fun verifyToken(token: String?): Boolean {
return try {
val claims = parseClaims(token)
claims != null && !
isExpired(token)
} catch (e: Exception) {
false
}
}
private fun parseClaims(token: String?): Claims? {
return try {
Jwts.parser()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.body
} catch (e: Exception) {
null
}
}
}