implementation 'com.auth0:java-jwt:4.2.1'
jwt:
secretKey: 512비트 이상의 문자
access:
expiration: 3600000 # 1시간(60분) (1000L(ms -> s) * 60L(s -> m) * 60L(m -> h))
header: Authorization
refresh:
expiration: 1209600000 # (1000L(ms -> s) * 60L(s -> m) * 60L(m -> h) * 24L(h -> 하루) * 14(2주))
header: Authorization-refresh
@Value("${jwt.secretKey}")
private String secretKey;
@Value("${jwt.access.expiration}")
private Long accessTokenExpirationPeriod;
@Value("${jwt.refresh.expiration}")
private Long refreshTokenExpirationPeriod;
@Value("${jwt.access.header}")
private String accessHeader;
@Value("${jwt.refresh.header}")
private String refreshHeader;
@Value를 사용하여 jwt설정파일 application-jwt.yml의 프로퍼티들을 주입
public String createAccessToken(String email) {
Date now = new Date();
return JWT.create()
.withSubject(ACCESS_TOKEN_SUBJECT)
.withExpiresAt(new Date(now.getTime() + accessTokenExpirationPeriod))
.withClaim(EMAIL_CLAIM, email)
.sign(Algorithm.HMAC512(secretKey));
}
public String createRefreshToken() {
Date now = new Date();
return JWT.create()
.withSubject(REFRESH_TOKEN_SUBJECT)
.withExpiresAt(new Date(now.getTime() + refreshTokenExpirationPeriod))
.sign(Algorithm.HMAC512(secretKey));
}
JWT.create로 JWT토큰을 생성하는 빌더를 생성.
.withSubject에는 생성할 값인 AccessToken과 RefreshToken을 각각 넣음.
.withExpiresAt은 설정해 놓은 토큰 만료 시간을 넣음(유효기간 설정)
.withClaim은 설정해놓은 클레임인 email을 넣어서 설정
(클레임은 Payload에 넣어 토큰에 담아 전송.)
.sign는 JWT토큰을 서명함. 이 때 사용되는 알고리즘은 HMAC512로, 이를 위해 secretKey값이 사용. 서명된 토큰은 문자열 형태로 반환.
public void sendAccessAndRefreshToken(HttpServletResponse response, String accessToken, String refreshToken) {
response.setStatus(HttpServletResponse.SC_OK);
setAccessTokenHeader(response, accessToken);
setRefreshTokenHeader(response, refreshToken);
}
public Optional<String> extractRefreshToken(HttpServletRequest request) {
return Optional.ofNullable(request.getHeader(refreshHeader))
.filter(refreshToken -> refreshToken.startsWith(BEARER))
.map(refreshToken -> refreshToken.replace(BEARER, ""));
}
public Optional<String> extractAccessToken(HttpServletRequest request) {
return Optional.ofNullable(request.getHeader(accessHeader))
.filter(refreshToken -> refreshToken.startsWith(BEARER))
.map(refreshToken -> refreshToken.replace(BEARER, ""));
}
Bearer~~에서 Bearer를 제외하고 순수 토큰만 가져오기 위해 replace로 Bearer삭제
public Optional<String> extractEmail(String accessToken) {
try {
return Optional.ofNullable(JWT.require(Algorithm.HMAC512(secretKey))
.build()
.verify(accessToken)
.getClaim(EMAIL_CLAIM)
.asString());
} catch (Exception e) {
log.error("액세스 토큰이 유효하지 않습니다.");
return Optional.empty();
}
}
토큰 유효성 검사하는데 사용할 알고리즘이 있는 JWT verifier builder 반환.
반환된 빌더로 JWT verifier 생성.
accessToken을 검증하고 유효하지않으면 예외 발생
getClaim으로 (Email)가져오기
public void setAccessTokenHeader(HttpServletResponse response, String accessToken) {
response.setHeader(accessHeader, accessToken);
}
public void setRefreshTokenHeader(HttpServletResponse response, String refreshToken) {
response.setHeader(refreshHeader, refreshToken);
}
public void updateRefreshToken(String email, String refreshToken) {
memberRepository.findByEmail(email)
.ifPresentOrElse(
user -> user.updateRefreshToken(refreshToken),
() -> new Exception("일치하는 회원이 없습니다.")
);
}
public boolean isTokenValid(String token) {
try {
JWT.require(Algorithm.HMAC512(secretKey)).build().verify(token);
return true;
} catch (Exception e) {
log.error("유효하지 않은 토큰입니다. {}", e.getMessage());
return false;
}
}