💌 Spring security 를 알아보며 자연스럽게 인증에 대해 알아볼수 밖에 없었다. 인증에 대해 알아보며 jjwt 라이브러리를 사용한 토큰 사용법에 대해 적고, 사소한 내용이지만 혹시라도 이 글을 보셔서 최소한 의 지식을 공유 했으면 좋겠다는 바람입니다.
JWT는 유저를 인증하고 식별하기 위한 토큰(Token) 기반 인증이다. 토큰 자체에 사용자의 권한 정보나 서비스를 사용하기 위한 정보가 포함된다. JWT를 사용하면 RESTful과 같은 무상태(Stateless)인 환경에서 사용자 데이터를 주고받을 수 있게 된다. 세션(Session)을 사용하게 될 경우에는 쿠키 등을 통해 사용자를 식별하고 서버에 세션을 저장했지만, JWT와 같은 토큰을 클라이언트에 저장하고 요청시 HTTP 헤더에 토큰을 첨부하는 것만으로도 단순하게 데이터를 요청하고 응답을 받아올 수 있다.
핵심 기능 으로는 3가지 - 키 생성, 토큰 생성, 토큰 읽기 기능으로 이루워 져있다.
그렇다면 이제부터 JWT 토큰 에 대해서 더 자세히 작성해 보겠습니다.
jjwt(java json web token) 라이브러리를 이용하여 JWT를 구현해 보려고 한다.
implementation ('io.jsonwebtoken:jjwt-api:0.12.5')
runtimeOnly ( 'io.jsonwebtoken:jjwt-impl:0.12.5',
'io.jsonwebtoken:jjwt-jackson:0.12.5'
)
Client =====> jjwt-api<-------- jjwt-impl
jjwt-api : 토큰 생성을 위한 객체들을 추상화 하기 위한 라이브러리
Client에서 인터페이스를 의존하게 함으로써 내부 로직을 캡슐화 하고 있음.
캡슐화에 대해 간단히 적어보자.
캡슐화 란?
데이터와, 데이터를 처리하는 행위를 묶고, 외부에는 그 행위를 보여주지 않는 것.
개념이 익숙하지 않다면, 해당 링크를 참고 해보길 바란다.
🟦dependency 설정과 동시에 의문이 생겼다. runtimeOnly에 왜 구분해서 넣었는가?
JWT는 Header, Payload, Signature로 구성된다.
Header.Payload.Signature
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJncmVlbm5ldXJvbkBlbWFpbC5jb20iLCJyb2xlIjoiUk9MRV9VU0VSIiwiaWF0IjoxNjc0ODg3NzM4LCJleHAiOjE2NzQ5NzQxMzh9.4koOlEtPTTObnMPk5xF3l2zuXRNnlkqYNg1eni8yit0
이 용어는 운송업에서 비롯하였는데, 지급(pay)해야 하는 적화물(load)을 의미한다.
즉, 사용에 있어서 전송되는 데이터라고 생각하면 된다.
사용 방식은 content 방식과 claim 방식이 있다.
=> Payload에 담겨 있는 key-value 값을 우리는 claim 이라고 부른다.
jjwt 를 처음 사용 할때는 JWTUtill 클래스를 만들어 생성, 검증의 코드를 작성하였다.
@Component
@Log4j2
public class JWTUtil {
@Value("${custom.jwt.secretKey}")
private static String SecretKey ;
public String createToken(Map<String,Object> valueMap, int min){
SecretKey key = null;
try {
key = Keys.hmacShaKeyFor(JWTUtil.SecretKey.getBytes("UTF-8"));
}catch (Exception e){
throw new RuntimeException(e.getMessage());
}
return Jwts.builder().header()
.add("typ","JWT")
.add("alg","HS256")
.and()
.issuedAt(Date.from(ZonedDateTime.now().toInstant()))
.expiration((Date.from(ZonedDateTime.now().plusMinutes(min).toInstant())))
.claims(valueMap) // 페이로드에 받은 정보를 넣는다.
.signWith(key) // 위에서 만는 키로 서명 .
.compact();
}
}
사용자가 비밀키를 임의로 String 값을 넣고 활용 하였다.
그렇다면 비밀키를 가지고 암호화/복호화 를 다 할 수 있는 알고리즘 기법은 무엇일까 ?
이를 알기 위해서는 우선 비대칭키 암호화 대칭키 암호화 에 대해서 알아야 한다.
대칭키 암호화 방식을 적용해보며 JWT에 대해 알았다.
@Value("${custom.jwt.secretKey}")
private static String SecretKey ;
key = Keys.hmacShaKeyFor(JWTUtil.Secretkey.getBytes("UTF-8"));
Keys.hmacShaKeyFor()을 사용해 시크릿 키 객체를 생성해 사용한다
HS256 즉 HmacSHA256을 적용하기 위해 256을 참고하였다
.add("typ","JWT")
.add("alg","HS256")
이렇게 만들어 진 key로 서명을 하면 되는 원리이다.
.signWith(key) // 위에서 만는 키로 서명 .
직접 Token 발급을 테스트 해보기 위해서 Controller를 작성해보았다.
@PostMapping("/make")
public ResponseEntity<Map<String,String>> makeToken(@RequestBody MemberDTO memberDTO){
log.info(".................make token...............");
MemberDTO result = memberService.read(memberDTO.getMid(),memberDTO.getMpw());
log.info(result);
String mid = result.getMid();
Map<String,Object> dataMap = result.getDataMap();
String accessToken = jwtUtil.createToken(dataMap,10);
String refreshToken = jwtUtil.createToken(Map.of("mid",mid),60*24*7);
log.info("accessToken : " + accessToken);
log.info("refreshToken : "+ refreshToken);
return ResponseEntity.ok(Map.of("accessToken",accessToken, "refreshToken",refreshToken));
}
🟦 참고
accessToken : 유효기간은 짧다.resfreshToken : 유효기간은 길다🟦 jwts
jwts 란
- JSON Web Token (JWT)을 생성하고 처리하기 위해 사용하는 유틸리티 클래스
- JWT의 생성, 서명, 검증 파싱 등 작업을 간편하게 수행할 수 있는 기능
PostMan Test

유효한 JWT인지 검증하는 것은 JwtParser 구현체가 담당하는 역할입니다.
1) Jwts 유틸리티 클래스를 이용해서 parser 작업을 하고 비밀키를 검증.
2) 주어진 토큰을 파싱하고, 검증된 클레임을 반환합니다.
3) JWT의 페이로드(클레임)를 반환하여, 이를 Claims 객체로 저장합니다.
검증 코드
public Map<String,Object> validateToken(String token){
SecretKey key = null;
try {
key = Keys.hmacShaKeyFor(JWTUtil.SecretKey.getBytes("UTF-8"));
}catch (Exception e){
throw new RuntimeException(e.getMessage());
}
Claims claims = Jwts.parser().verifyWith(key).build().parseEncryptedClaims(token).getPayload();
return claims;
}