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;
}
}