이전 포스트에서 JWT에 대한 기본적인 개념들과 저장 위치별 장단점을 확인 하였습니다.
이번 포스트 부터는 JWT를 생성, 검증, 폐기를 알아보겠습니다.
Language: java
FrameWork: spring 2.7.6
Build: Gradle
DataBase: MySQL, Redis
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
jwt를 사용하기 위해 위 3개의 의존성을 추가해주세요.
@GetMapping("/main/login")
public String login(@AuthenticationPrincipal UserDetails userDetails, HttpServletResponse response) {
if (userDetails != null) {
response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
response.setHeader("Location", "/main/index");
return null;
}
return "/main/login";
}
@ResponseBody
@PostMapping("/main/login")
public ResponseEntity<?> login(@RequestBody Login login, HttpServletResponse response) throws Exception {
User authenticatedUser = userService.loadUserByLoginId(login.getLoginId()).orElseThrow(
() -> new RuntimeException("해당 유저를 찾을 수 없습니다.")
);
final String JwtToken = jwtTokenUtil.generateToken(authenticatedUser);
Cookie jwtCookie = tokenService.createJwtCookie(authenticatedUser, JwtToken);
response.addCookie(jwtCookie);
return ResponseEntity.ok(jwtCookie);
}
위 코드를 보면 사용자가 로그인 페이지(/main/login)에 접근 요청(Get)을 보냈을 경우
사용자가 이미 로그인한 사용자는 main/index로 튕겨나갑니다.
로그인을 하지 않은 사용자라면 loginId와 loginPw를 입력하면 loadUserByLoginId 해당 유저를 검색 하고 해당하는 유저가 없다면 exception을 발생 시킵니다.
우리가 첫번째로 볼 메소드는 generateToken 입니다.
@Component
public class JwtTokenUtil {
public String generateToken(User user) {
return JWT.create()
.withSubject(user.getEmail())
.withExpiresAt(new Date(System.currentTimeMillis() + JwtProperties.EXPIRATION_TIME))
.withClaim("id", user.getId())
.withClaim("nickname", user.getName())
.sign(Algorithm.HMAC512(JwtProperties.SECRET));
}
}
위 코드에서는 User 객체로부터 이메일, ID, 닉네임 등의 정보를 가져와서 토큰에 포함시킵니다.
public interface JwtProperties {
String SECRET = "Secret Key";
int EXPIRATION_TIME = 864000000; //60000 1분 //864000000 10일
String TOKEN_PREFIX = "Bearer ";
String HEADER_STRING = "Authorization";
}
JWT의 특성을 정의한 인터페이스 입니다.
generateToken으로 jwt토큰이 생성 되었습니다.
생성된 토큰
- BearereyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJjNjU2MjFAbmF2ZXIuY29tIiwibmlja25hbWUiOiLsnbTtmLjsmIEiLCJpZCI6MSwiZXhwIjoxNjk0Njg0MjUzfQ.9SdxQMb6BX7WXJy6KXwlPP3iiPP81k8RWpatvzaHAX_k0ySSPm-hgT3JkR47K6mL1HLp8csMVNTIbeF75ufTvA
위 토큰을 jwt 링크에 들어가서 디코딩 하면 디코딩이 안됩니다.
왜? Bearer은 우리가 임의로 붙인 접두어 입니다. 제거 후 디코딩 하면 아래와 같이 표시됩니다.
HEADER
{
"typ": "JWT",
"alg": "HS512"
}
PAYLOAD
{
"sub": "test01@naver.com",
"nickname": "test01",
"id": 1,
"exp": 1694684253 //토큰 유효 시간 마우스를 가져다 대면 시간형식으로 출력
}
SIGNATURE
HMACSHA512(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
your-512-bit-secret
)
우리가 생성한 토큰입니다. 이 토큰을 Cookie에 저장해 봅시다.
public Cookie createJwtCookie(User authenticatedUser, String jwtToken) {
UserDetails userDetails = this.userService.loadUserByUsername(authenticatedUser.getLoginId());
Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAu```
코드를 입력하세요
```thentication(auth);
jwtToken = JwtProperties.TOKEN_PREFIX.trim() + jwtToken;
Cookie jwtCookie = new Cookie("Authorization", jwtToken);
jwtCookie.setHttpOnly(true);
jwtCookie.setMaxAge(JwtProperties.EXPIRATION_TIME);
jwtCookie.setPath("/");
return jwtCookie;
}
이렇게 생성된 Cookie객체를 응답으로 클라이언트에서 반환합니다.
response.addCookie(jwtCookie);
return ResponseEntity.ok(jwtCookie);
위에서 토큰을 생성하고 토큰을 Cookie객체에 담아 클라이언트에 보냈습니다.
우리는 이 토큰을 어디서 확인 할 수 있을까요???
저는 로그인 후 index로 이동하니 index기준으로 설명 드립니다.
크롬 또는 다른 웹 브라우저에서 개발자 도구를 들어갑니다.
Network탭에서 index를 선택 하고 Headers 탭 -> RequestHeader에 보면 아래와 같이 저장되었습니다.(Cookie 탭에 들어가서 확인하여도 됩니다.)
Cookie: