JJWT 의존성 오류
오래된 JJWT 대신 최신에도 업데이트 되는 JJWT API 활용하며 에러가 발생했습니다.
JJWT API 의존성 외에도 런타임에 활용될 JJWT Impl, JJWT GSON 의존성을 추가해야 합니다.
아래 블로그의 도움을 받았습니다.
JJWT Impl 의존성 추가 안했을 때
Unable to load class named [io.jsonwebtoken.impl.DefaultJwtBuilder] from the thread context, current, or system/application ClassLoaders. All heuristics have been exhausted. Class could not be found. Have you remembered to include the jjwt-impl.jar in your runtime classpath?
at io.jsonwebtoken.lang.Classes.forName(Classes.java:93) ~[jjwt-api-0.11.5.jar:0.11.5]
Jwts.compact() 실행시 impl 패키지 내의 DefaultJwtBuilder 인스턴스화 시도하기 때문에 JJWT Impl 의존성 필요
package io.jsonwebtoken;
import io.jsonwebtoken.lang.Classes;
public final class Jwts {
private static final Class[] MAP_ARG = new Class[]{Map.class};
private Jwts() {
}
public static JwtBuilder builder() {
return Classes.newInstance("io.jsonwebtoken.impl.DefaultJwtBuilder");
}
}
JJWT GSON 의존성 추가 안했을 때
JJWT Serializer 인터페이스는 활용하는데, 구현체가 없어서 예외가 발생합니다.
io.jsonwebtoken.lang.UnknownClassException: Unable to find an implementation for interface io.jsonwebtoken.io.Serializer using java.util.ServiceLoader.
Jwts.compact() -> DefaultJwtBuilder.compact()
public class DefaultJwtBuilder implements JwtBuilder {
private Header header;
private Claims claims;
private String payload;
private SignatureAlgorithm algorithm;
private Key key;
private Serializer<Map<String,?>> serializer;
@Override
public String compact() {
if (this.serializer == null) {
// try to find one based on the services available
// TODO: This util class will throw a UnavailableImplementationException here to retain behavior of previous version, remove in v1.0
// use the previous commented out line instead
// Serializer 인터페이스 로딩
this.serializer = LegacyServices.loadFirst(Serializer.class);
}
...
}
JJWT API의 인터페이스
package io.jsonwebtoken.io;
public interface Serializer<T> {
byte[] serialize(T t) throws SerializationException;
}
JJWT Jackson에 포함된 JJWT API의 인터페이스 구현체
package io.jsonwebtoken.jackson.io;
public class JacksonSerializer<T> implements Serializer<T> {
static final ObjectMapper DEFAULT_OBJECT_MAPPER = new ObjectMapper();
private final ObjectMapper objectMapper;
@SuppressWarnings("unused") //used via reflection by RuntimeClasspathDeserializerLocator
public JacksonSerializer() {
this(DEFAULT_OBJECT_MAPPER);
}
@SuppressWarnings("WeakerAccess") //intended for end-users to use when providing a custom ObjectMapper
public JacksonSerializer(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "ObjectMapper cannot be null.");
this.objectMapper = objectMapper;
}
@Override
public byte[] serialize(T t) throws SerializationException {
Assert.notNull(t, "Object to serialize cannot be null.");
try {
return writeValueAsBytes(t);
} catch (JsonProcessingException e) {
String msg = "Unable to serialize object: " + e.getMessage();
throw new SerializationException(msg, e);
}
}
@SuppressWarnings("WeakerAccess") //for testing
protected byte[] writeValueAsBytes(T t) throws JsonProcessingException {
return this.objectMapper.writeValueAsBytes(t);
}
}
WeakKeyException
HS512 알고리즘을 활용할 때 키는 512bits == 64bytes 보다 커야합니다.
키가 너무 짧아서 나는 예외입니다. 아래와 같이 적절한 키를 만들거나 임의의 64바이트를 넘는 값을 지정하시면 됩니다.
(영어, 숫자, 특수문자, 띄어쓰기, 엔터키 1바이트 / 한글 3바이트이지만 ... 키에 특수문자, 띄어쓰기 들어가면 예외터집니다.)
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
// 키를 Base64로 인코딩하여 출력 (예: 환경 변수 또는 설정 파일에 저장)
String base64EncodedKey = Encoders.BASE64.encode(key.getEncoded());