이전 글에서 JWT는 무엇이고 왜 사용하는지에 대해 설명하였다.
이번에는 JWT를 직접 구현해봄으로써, 어떤 동작원리를 가지고 있는지 이해해보려고 한다.
JAVA -JWT 라이브러리 추가
build.gradle
implementation 'com.auth0:java-jwt:4.4.0'
build.gradle에 아래와 같이 라이브러리를 추가한다.
다른 버전이나 다른 개발 환경에서 사용하고 싶다면 아래에서 jwt를 검색해서 사용하면 된다.
이번 연습 때는 jwt Service 영역을 만들고, Controller에 Rest 방식을 이용해 간단하게 데이터를 주고 받을 것이다.
테스트 환경으로는 Boomerang 크롬 확장자를 이용하여 데이터를 주고 받는다.
JWTService.java
package com.project.jwt.util;
import java.util.Date;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator.Builder;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.JWTVerifier;
public class JWTService {
private static String secretKey = "test1010"; // 시그니처를 만들기 위한 비밀키
// JWT 토큰 생성
public static String createToken(String username) {
//암호화알고리즘
Algorithm algorithm = Algorithm.HMAC256(secretKey);
//만료시간
long expire = System.currentTimeMillis() + 3600000; //1시간뒤
// 페이로드 생성
Builder builder = JWT.create()
.withSubject(username) // 토큰의 주제(subject) 설정
.withIssuedAt(new Date()) // 토큰 발급 시간 설정 (현재 시간)
.withExpiresAt(new Date(expire)) // 토큰 만료 시간 설정
.withIssuer("coding404서버") //토큰발행자
.withClaim("admin", "나야나~"); //공개클래임
return builder.sign(algorithm); // 비밀 키로 서명하여 토큰 생성
}
//토큰검증
public static boolean validateToken(String token) throws JWTVerificationException { //확인단계중하나라도 실패
Algorithm algorithm = Algorithm.HMAC256(secretKey); //검증 알고리즘
JWTVerifier verifier = JWT.require(algorithm).build(); //token을 검증할 객체생성
verifier.verify(token); // 토큰 검증을 수행하며, 만료 시간도 자동으로 검사됨
return true; // 검증 성공 시 true 반환
}
}
Signature를 위한 비밀키 생성
암호화 이전에 누가 사용하는지를 명시해줄 secretKey를 생성한다.
JWT 토큰 생성에 필요한 알고리즘 생성
Algorithm algorithm = Algorithm.HMAC256(secretKey);
만료 시간 생성
long expire = System.currentTimeMillis() + 3600000; //1시간뒤
토큰 생성
Builder builder = JWT.create()
.withSubject(username) // 토큰의 주제(subject) 설정
.withIssuedAt(new Date()) // 토큰 발급 시간 설정 (현재 시간)
.withExpiresAt(new Date(expire)) // 토큰 만료 시간 설정
.withIssuer("coding404서버") //토큰발행자
.withClaim("admin", "나야나~"); //공개클래임
토큰을 생성할 때 서명에 생성한 알고리즘을 이용
토큰 생성에 권장되는 미리 정의된 몇몇의 정보나, 넣고 싶은 정보를 정의해서 넣을 수도 있다.
위 순서는 이전 포스터에서 설명한 페이로드에 대한 내용에서 알 수 있고, 그 순서에 따라 값을 설명할 수 있다.
비밀 키로 서명하여 토큰 생성
return builder.sign(algorithm); // 비밀 키로 서명하여 토큰 생성
위 처럼 JWTService에서 토큰 생성에 대한 비즈니스로직이 완성되면 Controller를 통해서 사용자 요청에 따라 토큰을 발행할 수 있다.
APIController
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.project.jwt.command.MemberVO;
import com.project.jwt.util.JWTService;
@RestController
public class APIController {
// JWT - API 기반의 인증 정보를 처리할 때, 토큰 발급 ( 발행자 )
@PostMapping("/login")
public ResponseEntity<Object> login(@RequestBody MemberVO vo){
// 사용자 정보를 받아서 로그인 처리
// ok - 로그인 성공
// 토큰 발행
String token = JWTService.createToken(vo.getUsername());
return new ResponseEntity<>(token, HttpStatus.OK);
}
@PostMapping("/api/v1/getInfo")
public ResponseEntity<Object> getInfo(HttpServletRequest request){
// 클라이언트에서 토큰을 헤더라는 곳에 담는다.
// 토큰을 전달받아 유효성을 확인한 후 만료인지, 통과인지 확인
String token = request.getHeader("Authorization");
System.out.println(token);
// 토큰이 유효한지 확인,
try {
boolean result = JWTService.validateToken(token);
System.out.println("토큰의 무결성 여부 : " + result);
} catch (Exception e) {
e.printStackTrace(); //토큰이 위조 됐을 경우
return new ResponseEntity<>("토큰 위조", HttpStatus.UNAUTHORIZED); //토큰 위조
}
return new ResponseEntity<>("통과된 사람이면 여기에 회원정보 발급", HttpStatus.OK);
}
}
위 사진과 같이 부메랑을 이용해 사용자 요청 URL을 통해 json 형식의 파일로 요청을 하면
서버로 부터 토큰이 발행되는 것을 확인할 수 있다.
이 토큰을 사용하여, 복호화를 진행하고 싶으면 jwt 공홈을 이용해 확인할 수 있다.
토큰의 유효성 검사
JWTService
//토큰검증
public static boolean validateToken(String token) throws JWTVerificationException { //확인단계중하나라도 실패
Algorithm algorithm = Algorithm.HMAC256(secretKey); //검증 알고리즘
JWTVerifier verifier = JWT.require(algorithm).build(); //token을 검증할 객체생성
verifier.verify(token); // 토큰 검증을 수행하며, 만료 시간도 자동으로 검사됨
return true; // 검증 성공 시 true 반환
}
}
요청된 토큰에 대해 검증을 하는 코드이다.
코드는 Boolean 타입으로 위조 여부에 대한 값이 true / false만 반환한다.
검증 알고리즘을 통해, token을 검증할 객체를 생성한다.
APIController
@PostMapping("/api/v1/getInfo")
public ResponseEntity<Object> getInfo(HttpServletRequest request){
// 클라이언트에서 토큰을 헤더라는 곳에 담는다.
// 토큰을 전달받아 유효성을 확인한 후 만료인지, 통과인지 확인
String token = request.getHeader("Authorization");
System.out.println(token);
// 토큰이 유효한지 확인,
try {
boolean result = JWTService.validateToken(token);
System.out.println("토큰의 무결성 여부 : " + result);
} catch (Exception e) {
e.printStackTrace(); //토큰이 위조 됐을 경우
return new ResponseEntity<>("토큰 위조", HttpStatus.UNAUTHORIZED); //토큰 위조
}
return new ResponseEntity<>("통과된 사람이면 여기에 회원정보 발급", HttpStatus.OK);
}
위 처럼 "/api/v1/getInfo" 라는 요청 url이 Post방식으로 들어오게 되면 토큰 유효성 검증을 실시하게 된다.
Header에는 여러 가지 값들이 담기긴 하지만 대표적으로 "Authorization"에 이전에 발급된 토큰의 값을 전달하게 된다.
'Programming > Spring Boot' 카테고리의 다른 글
[Spring Boot] Spring Security를 이용한 JWT CORS Filter 로그인 커스터마이징하기 (0) | 2024.05.30 |
---|---|
[Spring Boot] Spring Security를 이용한 JWT CORS필터체이닝 (0) | 2024.05.30 |
[Spring Boot] JWT (1) | 2024.05.30 |
[Spring Boot] 어노테이션을 활용해 권한 설정하기 (0) | 2024.05.29 |
[Spring Boot] Spring Security 로그인 처리 (1) | 2024.05.29 |