Session을 사용하는 방식의 경우 Stateful한 상태이기 때문에 수평확장에 어려움이 가지고 있어 이를 대신하여 Rest API에서 Stateless 상태를 유지하고 서버에서 사용자를 인증할 수 있는 JSON 포맷을 사용한 인증수단이다. JWT는 Header, Payload, Signature 세 부분으로 구성되어 있고 실제 JWT를 전달할 때는 Base64 Url-Safe 방식으로 인코딩을 하여 전달한다.
yml 파일에서 JWT 토큰 설정을 가져오는 빈을 설정한다.
@Component
@ConfigurationProperties(prefix = "jwt")
public class JwtConfigure {
private String header;
private String issuer;
private String clientSecret;
private int expirySeconds;
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
public String getIssuer() {
return issuer;
}
public void setIssuer(String issuer) {
this.issuer = issuer;
}
public String getClientSecret() {
return clientSecret;
}
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
public int getExpirySeconds() {
return expirySeconds;
}
public void setExpirySeconds(int expirySeconds) {
this.expirySeconds = expirySeconds;
}
}
JWT 토큰설정을 참고하여 JWT 객체를 생성하는 빈을 설정한다.
@Bean
public Jwt jwt(JwtConfigure jwtConfigure) {
return new Jwt(
jwtConfigure.getIssuer(),
jwtConfigure.getClientSecret(),
jwtConfigure.getExpirySeconds()
);
}
JWT 발행을 위한 sign 메소드와 JWT 검증을 위한 verify 메소드를 작성한다.
public final class Jwt {
private final String issuer;
private final String clientSecret;
private final int expirySeconds;
private final Algorithm algorithm;
private final JWTVerifier jwtVerifier;
public Jwt(String issuer, String clientSecret, int expirySeconds) {
this.issuer = issuer;
this.clientSecret = clientSecret;
this.expirySeconds = expirySeconds;
this.algorithm = Algorithm.HMAC512(clientSecret);
this.jwtVerifier = com.auth0.jwt.JWT.require(algorithm)
.withIssuer(issuer)
.build();
}
public String sign(Claims claims) {
Date now = new Date();
JWTCreator.Builder builder = com.auth0.jwt.JWT.create();
builder.withIssuer(issuer);
builder.withIssuedAt(now);
if (expirySeconds > 0) {
builder.withExpiresAt(new Date(now.getTime() + expirySeconds * 1_000L));
}
builder.withClaim("username", claims.username);
builder.withArrayClaim("roles", claims.roles);
return builder.sign(algorithm);
}
public Claims verify(String token) throws JWTVerificationException {
return new Claims(jwtVerifier.verify(token));
}
// ... 생략 ...
static public class Claims {/*생략*/}
}