
JWT란?
Json Web Token 를 뜻하며 기업에서 많이 사용 중인 토큰 방식의 로그인 구현을 위한 토큰 발행 API이다. 예전에 프로젝트를 할 때 NodeJS 환경에서 발급하였는데 이번에는 Spring 에서 발급 및 해독하는 방법을 코드단위에서 알아보려고 한다.
JWT의 구성과 작동 방식은 다음번에 라이브러리 없이 직접 발행하는 과정을 실습하며 포스팅할 예정이다.
Project 환경
SpringBootMavenJava 11Dependency 설정
SpringBoot initializer 로 프로젝트 생성시에 JWT 관련 모듈을 미리 다운받아도 되고, xml 파일에 직접 작성해주고 리로드해도 상관없다. 글쓴이는 미리 생성해둔 프로젝트에 모듈을 미리 다운받지 않았기 때문에 다음과 같이 수동으로 코드를 추가해 주었다.
#pom.xml
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
이로써 우리는 "Jwts"라는 Api를 Spring 에서 사용할 수 있게 되었다.
JWT 토큰 생성
JWT 토큰은 Header, Payload(claim), Signature 로 이루어져 있으며 최종 결과물에서 "."으로 구분한다. 자세한 내용은 다음 포스트에서 다룰 것이므로 이해가 안간다면 우선 외우자!
다음은 아까 추가하였던 Jwts 모듈을 호출하여 JWT 토큰을 생성하는 예제이다.
@Slf4j
@Controller
@RequestMapping("/v5")
public class JwtTestController {
@GetMapping("/jwt")
public String jwtTestForm() {
return "v5/jwtTest";
}
@PostMapping("/jwt")
public String successForm(Model model, @RequestParam String email, String username) {
//salt
String salt = "saaalt";
//make jwt header
Map<String, Object> jwtHeader = new HashMap<>();
jwtHeader.put("typ", "JWT");
jwtHeader.put("alg", "HS256");
jwtHeader.put("regDate", System.currentTimeMillis());
//make claim
Map<String, Object> claim = new HashMap<>();
claim.put("email", email);
claim.put("username", username);
String token = Jwts.builder()
.setSubject(email)
.setHeader(jwtHeader)
.setClaims(claim)
.signWith(SignatureAlgorithm.HS256, salt)
.compact();
model.addAttribute("token", token);
return "/v5/verifyTest";
}
}
본인은 v5/verifyTest 라는 jsp 페이지로 토큰을 내려주어 시각적으로 확인하기 위한 컨트롤러를 작성하였다. (아무 설명없이 토큰 발행 코드만 제시하면 오해의 소지가 있을 것 같아 컨트롤러 전문을 가져왔다.)
발행시에는 위에서 언급했던 Header, Payload(claim), Signature 를 만들어주는 과정이 들어간다.
Header 에는 이 토큰이 JWT 토큰임을 밝히며(typ) 내가 사용하고 싶은 시그니처의 암호화 방식(alg)을 명시한다.
Payload(claim) 부분에서는 내가 실제로 이 유저를 식별할 수 있는 "최소한" 의 정보만 담아준다. "최소한" 이라고 한 이유는 JWT 는 앞의 두 정보 즉 헤더와 페이로드를 단순히 base64 방식으로만 인코딩해서 전달하고 실질적인 암호화는 Signature 부분에서만 일어나기 때문에 유저 민감 정보를 "절대로" 담아서 서비스 하면 안된다.
JWT 토큰 해독
토큰 해독도 마찬가지로 Jwts 모듈에서 Signature를 만들때 사용했던 secret key(salt)를 첨가해주면 된다.
@Controller
@RequestMapping("/v5")
public class JwtVerifyController {
@PostMapping("/verify")
public String verifyJWT(Model model, @RequestParam String token) {
Claims verified = Jwts.parser().setSigningKey("saaalt").parseClaimsJws(token).getBody();
model.addAttribute("userInfo", verified);
return "/v5/verifyResult";
}
}
마찬가지로 v5/verifyResult 라는 페이지에서 시각적으로 확인하기 위하여 컨트롤러를 작성하였다. 해독과정은 미리 다 지정해준 방식으로 되돌리기만 하면 되기 때문에 비교적 간단하다. 그리고 실험은 해보지 않았지만 HS256방식이 JWT의 디폴트 알고리즘이기 때문에 파싱과정에서 별다른 알고리즘을 기입하지 않아도 잘 작동했던것 같다. (예전에 NodeJS 에서는 SSL 인증서 방식으로도 해보았는데 그런과정이 없었기 때문에..)
3줄 요약
JWT 는 유명한 웹 토큰의 한 종류이다.Header, Payload(claim), Signature 로 이루어져있다.payload에 넣지 말자!